Observable StoreLoading saved progress…

Observable Store

Redux, Zustand, and every "global state" hook share a tiny core: a single object holding state, a way to update it, and a list of listeners that fire whenever it changes. Strip away the middleware, selectors, and React glue and you're left with the observable store — maybe forty lines. Building it by hand is the fastest way to understand what those libraries actually do.

Implement observableStore(initialState). It returns an object with getState(), setState(update), and subscribe(listener), where setState merges an update and notifies every subscriber.

Signature

function observableStore(initialState) {
  return {
    getState,   // () => state
    setState,   // (partial | (prev) => partial) => void
    subscribe,  // (listener) => unsubscribe
  };
}

Examples

const store = observableStore({ count: 0, name: 'a' });
const off = store.subscribe((next, prev) => console.log(prev, '->', next));

store.setState({ count: 1 });            // logs {count:0,name:'a'} -> {count:1,name:'a'}
store.setState((s) => ({ count: s.count + 1 })); // updater form -> count 2
store.getState();                        // { count: 2, name: 'a' }
off();                                    // stop listening

Notes

  • Shallow mergesetState({ count: 1 }) keeps the other keys. The updater form (prev) => partial computes the patch from the current state.
  • Immutable swap — produce a new state object each update; never mutate the old one, so a captured prevState stays a valid snapshot.
  • Notify with both states — call each listener as listener(nextState, prevState).
  • Snapshot listeners before notifying — subscribing or unsubscribing inside a listener must not skip or double-run others in the same round.
Loading editor…