Cubit is the class you subclass for almost everything in BlaC. This page is the class reference: the signature, its members, and the mutation semantics. For narrative (“why a class”, getters, args/deps lifecycle, async patterns) see Mental Model, Passing Inputs, and Best Practices.
classCubit<
Sextendsobject=any,
Args=void,
Depsextendsobject=Record<string,never>,
>extends StateContainer<S,Args,Deps>{}
Type parameter
Default
Description
S
any
The state shape. Must be an object type (S extends object).
Args
void
Serializable construction/identity data delivered to init(args). See Passing Inputs.
Deps
Record<string, never>
Non-serializable handles injected per consumer, read via this.deps.
Cubit has an empty class body — it exists only so instance instanceof Cubit works. Every member below is declared on StateContainer; Cubit adds nothing.
The registry always builds instances zero-arg, so your subclass constructor takes no parameters of its own — it just calls super(initialState). Runs before init(args); use it solely to set the initial state.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks.
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). A caller that
wants "skip if no real change" patch semantics can wrap patch
themselves or call emit after a manual equality check.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks.
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). A caller that
wants "skip if no real change" patch semantics can wrap patch
themselves or call emit after a manual equality check.
No-op by default. Fires after each deps merge, and once more on dispose with everything cleared.
Also inherited from StructuralContainer<S> (not re-declared): state getter, channel, subscribe(interest, cb). Out of scope here — see watch: the low-level channel.subscribe.
State is immutable from the outside — you never assign to this.state.x. Each method below hands the container a new value; the container diffs it against the previous one and wakes only the consumers whose read paths moved.
Method
Signature
Semantics
emit
emit(next: S): void
Full replace — does not merge. No-op if next is reference-equal to the current state, or if the configured equality function reports them equal.
update
update(fn: (state: S) => S): void
Sugar over emit: calls this.emit(fn(currentState)). fn must return the full next state, and inherits emit’s equality short-circuit.
patch
patch(partial: DeepPartial<S>): void
Deep merges along plain-object branches. Arrays, Date, Map, Set, and class instances are atomic leaves.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks.
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). A caller that
wants "skip if no real change" patch semantics can wrap patch
themselves or call emit after a manual equality check.
Cubit<S> is a StateContainer<S> with emit / patch exposed as
public mutation surface. Today it adds nothing structurally beyond
StateContainer — both are inherited from the underlying
StructuralContainer<S>. Kept as a real class (not a type alias) because
downstream code does instance instanceof Cubit checks.
The class body is intentionally empty: a no-op emit override would
still go through applyState, and patch is inherited from
StructuralContainer (path-diffed, microtask-flushed). A caller that
wants "skip if no real change" patch semantics can wrap patch
themselves or call emit after a manual equality check.
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.
Every mutator hands the container a new value rather than mutating in place. This is not a style preference — it’s what the diff-and-wake mechanism needs: tracking records which paths a consumer read, and on the next mutation the container compares old-state to new-state to find which paths moved. Mutating this.state directly leaves nothing to diff against, so the container has no way to know what changed (or that anything changed at all). Nested objects/arrays follow the same rule down the tree: replace the containing object at whatever depth you changed a leaf, rather than mutating that leaf’s parent in place.