Internals: Mutation & State Transition
SoulState follows a strict Immutable Update Pattern. State transitions are not just about changing values; they are the triggers for the entire Invalidation & Propagation Lifecycle.
The Mutation Lifecycle
When setState is called, SoulState performs a multi-step transition:
- Reconciliation: The new partial state is merged with the current state to create a new root object.
- Key Detection: SoulState identifies which top-level keys changed by comparing the old and new root properties via
Object.is. - Invalidation Triggering: The changed keys are sent to the Invalidation Graph.
- Propagation: The
Runtimeschedules a flush to notify affected nodes in topological order.
// Internal transition logic (simplified)
// src/core/store.ts setState handler
const nextState = { ...currentState, ...partialUpdate };
const changedKeys: DependencyKey[] = [];
// Surgical key detection via Object.is
for (const key of Object.keys(nextState)) {
if (!objectIs(currentState[key], nextState[key])) {
changedKeys.push(key);
}
}
if (changedKeys.length > 0) {
runtime.setState(nextState, changedKeys);
}
Change Detection Strategy
SoulState performs Surgical Detection at the root key level.
- Fast-Path: If
Object.is(currentState, nextState)is true, the update is ignored immediately. - Granular Tracking: If
state.useris updated, the engine marks theuserkey as dirty. Any node in the graph that accessedstate.userduring its tracking phase is now a candidate for re-computation.
Structural Sharing
SoulState leverages standard JavaScript structural sharing. Because state is immutable:
- Unchanged branches of the state tree maintain their reference identity.
- Change detection at any level of the tree is a constant-time reference check.
- React's
useSyncExternalStorecan efficiently determine if a component needs to re-render.
Invalidation Guard
To prevent infinite loops, SoulState's mutation engine includes a Flush Depth Guard. If an update triggers a chain of reactive changes that exceeds 100 cycles, the runtime throws an error to protect the main thread.
Deterministic Ordering
The mutation engine ensures that the state is fully reconciled before any listeners are notified. This guarantees that any code running inside a listener or computed selector always sees a consistent, settled view of the world.