A Cubit is a state container that holds a typed state value and exposes methods to change it. It is the class you subclass for almost everything in BlaC. Every signature on this page is quoted from the @blac/core source.
classCubit<
Sextendsobject=any,
Args=void,
Depsextendsobject=Record<string, never>,
> extendsStateContainer<S, Args, Deps> {
emit(next:S):void;
patch(partial:DeepPartial<S>):void;
update(fn:(state:S)=>S):void;
}
Type parameter
Default
Description
S
any
The state shape. Must be an object type (S extends object); primitives like number or string are not supported as state.
Args
void
Serializable construction data delivered to init(args). See Args.
Deps
Record<string, never>
Non-serializable handles injected per consumer and read via this.deps. See Deps.
Cubit re-declares emit, patch and update as public; on StateContainer the three are protected. That is the difference between them: a StateContainer owns its own transitions and callers go through the methods it exposes, while a Cubit can also be driven from outside. Extend Cubit unless you specifically want mutation sealed inside the class.
BlaC has exactly two base types: StateContainer (the abstract engine) and Cubit (the concrete class you extend). There is no Bloc class — if you are coming from flutter_bloc, the closest equivalent is Cubit. We use the name “bloc” colloquially to mean “any state-container instance,” but the only thing you ever extend is Cubit. See Core Concepts for the full StateContainer / Cubit / bloc / instance hierarchy.
The state lives in a class for three concrete reasons:
State and the logic that changes it live together. Methods like addItem sit next to the state they mutate, so an action is a method call, not a reducer plus an action-type constant plus a dispatch.
Derived values are just getters. A get total() is computed on read and tracked automatically — no selector library, no memo wiring (see Getters).
Instances have identity and a lifecycle. The registry can create, key, ref-count, and dispose instances. That is what makes shared-vs-scoped state, args, and deps possible (see Instance Management).
For the deeper rationale (why proxy tracking over selectors, why microtask batching, how this compares to Redux/Zustand/MobX), see the Mental Model.
Low-level container options (custom per-path equality, skeleton hints). Most subclasses omit it.
Returns: a new instance. The registry always builds instances zero-arg, so your subclass constructor takes no arguments — it only calls super(initialState).
Behavior. Runs before init(args). Use it solely to set the initial state; args-derived setup belongs in init.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Override of StructuralContainer.patch that routes through the
StateContainer concerns: disposed guard, dev-only emit-rate check,
_changedWhileHydrating flag, pending-change capture (so stateChanged
system events see the merged prev/next), and the registry-level
stateChanged notification. We still call
super.patch so path-marking semantics (the whole point of patch) are
preserved.
State in BlaC is immutable from the outside: you never assign to this.state.x. Instead you hand the container a new state and it diffs the change, marks which paths moved, and wakes only the consumers that read those paths. The three methods below are three ways to produce that next state.
Replace the entire state. Use when you have the full new state ready.
emit(next: S): void
Parameter
Type
Required
Description
next
S
yes
The complete new state. Replaces the current state wholesale — it does not merge.
Returns:void.
Behavior.emit is a no-op when the next state is equal to the current one: it short-circuits if prev === next by reference, or if the configured equality function (default: shallow per-key Object.is) reports them equal. So emitting an object that happens to match the current state will not wake any consumers. Equality is configurable per class via blac({ equality }) and globally via configureBlac — see Configuration.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Derive new state from the current state. Use when you need to read the current state to compute the next one.
update(fn: (state:S)=>S): void
Parameter
Type
Required
Description
fn
(state: S) => S
yes
A reducer receiving the current state and returning the complete next state.
Returns:void.
Behavior.update is sugar over emit: it calls this.emit(fn(currentState)), so it inherits emit’s equality short-circuit. Your fn must return the full next state — the same “no merge” rule as emit applies, so spread the existing state when you only change a few fields.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Deep-merge partial changes into the current state. Use when you want to update some fields without touching others.
patch(partial: DeepPartial<S>): void
Parameter
Type
Required
Description
partial
DeepPartial<S>
yes
The subtree to merge. Nested objects can be patched without spreading the full structure; arrays, Date, Map, Set, and class instances are treated as atomic leaves.
Returns:void.
Behavior.patch deep-merges along plain-object branches and value-filters the result: a path is marked dirty only if its value actually changed. It skips the update entirely if all provided top-level values are already Object.is-equal to the current state (a shallow pre-spread no-op check), and deepMerge also returns the previous state by reference on a deep no-op.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Override of StructuralContainer.patch that routes through the
StateContainer concerns: disposed guard, dev-only emit-rate check,
_changedWhileHydrating flag, pending-change capture (so stateChanged
system events see the merged prev/next), and the registry-level
stateChanged notification. We still call
super.patch so path-marking semantics (the whole point of patch) are
preserved.
patch({
loading?: boolean |undefined
loading: true });
};
ProfileCubit.rename: (name:string)=>void
rename=(
name: string
name:string)=> {
// Only updates user.profile.name — age and other fields are preserved
Override of StructuralContainer.patch that routes through the
StateContainer concerns: disposed guard, dev-only emit-rate check,
_changedWhileHydrating flag, pending-change capture (so stateChanged
system events see the merged prev/next), and the registry-level
stateChanged notification. We still call
super.patch so path-marking semantics (the whole point of patch) are
preserved.
Getters are how you model derived state — values computed from other state rather than stored. Prefer a getter over storing a computed field: a stored total can drift out of sync with items, but a get total() is recomputed on every read and can never be stale.
A getter has no signature fence of its own — it is plain TypeScript on your subclass. In React render, it participates in auto-tracking:
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.
@param ― callbackfn A function that accepts up to four arguments. The reduce method calls the callbackfn function one time for each element in the array.
@param ― initialValue If initialValue is specified, it is used as the initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.
A Cubit instance moves through a fixed sequence, and the input hooks slot into specific points:
Construct — new Type() runs your constructor, which calls super(initialState). The constructor takes no arguments; the registry always builds instances zero-arg.
init(args) — called once, synchronously, after construction and before any consumer reads the first snapshot. This is where args-derived setup belongs.
onDepsChanged(next, prev) — called whenever the merged per-consumer deps view changes (and once more on dispose, with everything cleared).
Mutations — emit / update / patch run as your methods are called.
Dispose — when the last consumer releases the instance (unless keepAlive); fires the dispose system event.
Steps 2 and 3 are the two input lanes: args for serializable identity data, deps for live non-serializable handles. The rest of this section covers each.
Seed args-derived state or kick off loads, once per instance, before the first snapshot.
protected init(args: Args): void
Parameter
Type
Required
Description
args
Args
yes
The args passed at acquire time (useBloc(Type, { args })). void when no Args generic is declared.
Returns:void.
Behavior. A protected lifecycle method — not callable from outside the class. The framework calls it once per instance, synchronously after new Type() and before the first state snapshot is read. It replaces the old setConfig/setProps patterns. When useBloc is called with { args }, those args are required at the call site (type error if omitted when Args != void), used to derive the instance identity (different args ⇒ different instance), and available synchronously in init before any consumer sees state. See Passing Inputs for the full args/identity model.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Cubit<
interface UserCardState
UserCardState, {
userId: string
userId:string }> {
constructor() {
super({
UserCardState.name: string
name: '',
UserCardState.loading: boolean
loading: false });
}
// Constructor stays zero-arg. The framework calls init(args) before first snapshot.
protected
UserCardCubit.init(args: {
userId: string;
}): void
Called once after construction with the args passed at acquire time, before the first
state snapshot is read by any consumer. Override to seed args-derived state (via
this.emit(...)) or kick off loads.
Override of StructuralContainer.patch that routes through the
StateContainer concerns: disposed guard, dev-only emit-rate check,
_changedWhileHydrating flag, pending-change capture (so stateChanged
system events see the merged prev/next), and the registry-level
stateChanged notification. We still call
super.patch so path-marking semantics (the whole point of patch) are
preserved.
patch({
loading?: boolean |undefined
loading: true });
}
// Side effects (fetch/subscribe/timer) belong here, not in init — see below.
Called when this instance goes from unowned to owned (first ref or first
depend() dependent). Override to start work that should only run while
something is actually using the instance — fetches, timers, listeners.
signal aborts on the matching
onDeactivate
and on dispose, so
async work started here can be cancelled without extra bookkeeping.
Runs before the first useBlocDeps slice is applied, so this.deps is
empty on first activation — use onDepsChanged for deps-driven work.
onActivate(
signal: AbortSignal
signal:
interface AbortSignal
The AbortSignal interface represents a signal object that allows you to communicate with an asynchronous operation (such as a fetch request) and abort it if required via an AbortController object.
Behavior.onActivate fires on the instance’s 0→1 ownership transition — the moment something first acquires it — and is the recommended place for side effects (fetch, subscribe, start a timer). It hands you an AbortSignal that is aborted when onDeactivate fires on the matching 1→0 transition, so cleanup is one line:
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Called when this instance goes from unowned to owned (first ref or first
depend() dependent). Override to start work that should only run while
something is actually using the instance — fetches, timers, listeners.
signal aborts on the matching
onDeactivate
and on dispose, so
async work started here can be cancelled without extra bookkeeping.
Runs before the first useBlocDeps slice is applied, so this.deps is
empty on first activation — use onDepsChanged for deps-driven work.
onActivate(
signal: AbortSignal
signal:
interface AbortSignal
The AbortSignal interface represents a signal object that allows you to communicate with an asynchronous operation (such as a fetch request) and abort it if required via an AbortController object.
Override of StructuralContainer.patch that routes through the
StateContainer concerns: disposed guard, dev-only emit-rate check,
_changedWhileHydrating flag, pending-change capture (so stateChanged
system events see the merged prev/next), and the registry-level
stateChanged notification. We still call
super.patch so path-marking semantics (the whole point of patch) are
preserved.
patch(
user: any
user));
}
}
init is not deprecated and keeps working exactly as documented above — it’s still the right place to seed synchronous state from args. Only the recommendation for side effects moves: onActivate fires only when something actually owns the instance, so work started there is never wasted on a discarded or SSR-only render.
By default, instance identity is the structural hash of all args. Override with a static property on the class to control exactly which args distinguish one instance from another:
static key: (args:Args)=> string
Behavior. A function (args: Args) => string declared once on the class, not at every call site. When absent, BlaC hashes the full args object. Args not referenced by key ride along as config but do not fork instances.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Declare a Deps type as the third generic parameter to receive non-serializable values (refs, callbacks, controller instances) that can’t go in args. Deps are read lazily via this.deps.x and may be undefined — always guard.
get deps(): Readonly<Deps>
Returns: a Readonly<Deps> view — the union of every consumer’s contributed slice.
Key properties of deps:
Never key identity — different refs don’t fork the instance.
Per-consumer merged — each useBloc call contributes its own slice; the bloc sees the union (last writer wins on key collisions).
Live — updated after each commit; may change over time. May legitimately be undefined, so always optional-chain.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Cubit<
interface UploadState
UploadState,
{
endpoint: string
endpoint:string }, // Args
{
inputRef?: InputRef |undefined
inputRef?:
interface InputRef
InputRef } // Deps
> {
private
FileUploadCubit.endpoint: string
endpoint='';
constructor() {
super({
UploadState.progress: number
progress: 0 });
}
protected
FileUploadCubit.init(args: {
endpoint: string;
}): void
Called once after construction with the args passed at acquire time, before the first
state snapshot is read by any consumer. Override to seed args-derived state (via
this.emit(...)) or kick off loads.
For handles that require initialization when they arrive (a canvas element, a rich-text-editor controller), implement onDepsChanged. It fires after each deps merge with the new and previous combined views.
The merged view before it. Compare the two to detect arrivals and departures.
Returns:void.
Behavior. Optional — blocs that don’t declare it just read this.deps.x lazily. When declared, it gives the bloc clean acquire/release edges without any consumer-side cleanup wiring. It also fires once on dispose with every key cleared (so next.x is undefined), giving you a teardown edge.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Cubit<
interface RenderState
RenderState,
{
sceneId: string
sceneId:string },
{
canvas?: HTMLCanvasElement |undefined
canvas?:
interface HTMLCanvasElement
The HTMLCanvasElement interface provides properties and methods for manipulating the layout and presentation of elements. The HTMLCanvasElement interface also inherits the properties and methods of the HTMLElement interface.
The HTMLCanvasElement interface provides properties and methods for manipulating the layout and presentation of elements. The HTMLCanvasElement interface also inherits the properties and methods of the HTMLElement interface.
Args identifying which keyed instance to resolve when an accessor is called without its own args.
Returns: a DepHandle<T> with two accessors — handle.untracked() resolves the dep against the registry lazily on each call (this.user.untracked()), and handle.track() does the same plus subscribes the reading React consumer. Both take an optional { args } to override defaultArgs per call. See Auto-tracking with .track().
Behavior. Records the dependency, then returns the handle. Resolving on every call keeps the surface immune to dep-instance churn. Note: .untracked() does not auto-resubscribe to the dep’s channel — consumers that need reactive updates use .track() (or subscribe explicitly via useBloc’s tracker). A naive always-on auto-bridge would cycle on mutual deps, which is why tracking is opt-in.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Declare a cross-bloc dependency. Returns a branded handle with two
accessors — the dep instance is resolved against the registry on each call,
which keeps the surface immune to dep-instance churn:
handle.track(options?) — reactive read. Returns [state, instance].
Inside a getter reached through the React proxy this subscribes the
reading component to the dep's changes (base impl: live, no subscription).
handle.untracked(options?) — returns the live instance with no tracking,
for imperative method calls and one-off reads.
defaultArgs resolves the dep instance when an accessor is called without
its own args; per-call options.args overrides it and can derive from
current state. This does NOT auto-resubscribe outside the React proxy;
non-React consumers needing updates should subscribe explicitly.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Override of StructuralContainer.patch that routes through the
StateContainer concerns: disposed guard, dev-only emit-rate check,
_changedWhileHydrating flag, pending-change capture (so stateChanged
system events see the merged prev/next), and the registry-level
stateChanged notification. We still call
super.patch so path-marking semantics (the whole point of patch) are
preserved.
Override of StructuralContainer.patch that routes through the
StateContainer concerns: disposed guard, dev-only emit-rate check,
_changedWhileHydrating flag, pending-change capture (so stateChanged
system events see the merged prev/next), and the registry-level
stateChanged notification. We still call
super.patch so path-marking semantics (the whole point of patch) are
preserved.
Allows manipulation and formatting of text strings and determination and location of substrings within strings.
String(
function(localvar)e: unknown
e) });
}
};
}
The requestId pattern discards responses from superseded requests. Each new load() call increments the ID, and callbacks from previous calls see a mismatch and bail out.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Cubit<S> is a StateContainer<S> that exposes mutation publicly.
That is the whole difference, and it is a real one: on StateContaineremit / patch / update are protected, so a container mutates itself
from its own methods and callers go through the API it chooses to publish.
Cubit re-declares the three as public for the cases where a caller
legitimately drives state from outside — test helpers, devtools
time-travel, benchmarks.
Reach for StateContainer when business logic should live in the class,
and Cubit when the caller owns the transitions.
Cubit<{
items: string[]
items:string[];
filter: "all"|"done"
filter:'all'|'done';
}> {
constructor() {
super({
items: string[]
items: [],
filter: "all"|"done"
filter: 'all' });
}
// state: { items: [...], filter: 'all' }
TodoCubit.bad: ()=>void
bad=()=> {
// @ts-expect-error — items is required; emit replaces the whole state
Override of StructuralContainer.patch that routes through the
StateContainer concerns: disposed guard, dev-only emit-rate check,
_changedWhileHydrating flag, pending-change capture (so stateChanged
system events see the merged prev/next), and the registry-level
stateChanged notification. We still call
super.patch so path-marking semantics (the whole point of patch) are
preserved.
Symptom: DevTools shows a new bloc instance being created and destroyed on every render, or the circuit-breaker throws “max instances exceeded.”
Cause: A non-serializable value (function, ref, class instance) is in args. Because args must be JSON-serializable, a fresh object reference each render produces a different hash and therefore a different instance key.
Fix: Move non-serializable values to the bloc’s Deps lane — they never affect instance identity:
// Non-serializable in args → new instance every render (throws in dev)