Skip to main content

API: subscribe

The low-level subscription API provides granular control over state change notifications outside of React.

store.subscribe()

Registers a listener function that is called when a selected slice of state changes.

Signature

function subscribe<S>(
selector: (state: T) => S,
listener: (selectedState: S, prevSelectedState: S) => void,
options?: SubscribeOptions<S>
): () => void;

Parameters

  1. selector: A function to pick data from the state. Like useStore, this is automatically tracked.
  2. listener: A function called whenever the selected value changes.
  3. options:
    • equalityFn: Comparison function (defaults to Object.is).

Returns

A teardown function (() => void) that unregisters the listener and removes its node from the Invalidation Graph.


The Invalidation Flow

When you subscribe to s => s.user.name, SoulState:

  1. Tracks: Executes the selector once to identify accessed keys (user).
  2. Registers: Creates a node in the InvalidationGraph mapping userlistener.
  3. Monitors: When setState includes user, the InvalidationGraph identifies your listener as an affected node.
  4. Re-evaluates: The selector is re-run. If the result changed (per equalityFn), the listener is triggered.

Irrelevant Update Elimination

If your selector does not access a key (e.g., s => s.count), changes to user will never trigger your listener. The InvalidationGraph skips unrelated nodes entirely, achieving $O(M)$ performance.

// Surgical Subscription
const unsub = store.subscribe(
s => s.user.status,
(status) => console.log('Status is now:', status)
);

// This triggers the listener
store.setState({ user: { ...state.user, status: 'away' } });

// This DOES NOT trigger the listener (completely ignored by the engine)
store.setState({ count: 10 });

Cleanup & Teardown

Removing a subscription is an O(1) operation.

const unsub = store.subscribe(s => s.a, () => {});

// Later:
unsub(); // O(1) removal from graph and linked-list
⚠️

Manual Teardown

Outside of React, you are responsible for calling the teardown function. Failure to do so will cause the InvalidationGraph to grow indefinitely, leading to memory leaks.