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.
function observableStore(initialState) {
return {
getState, // () => state
setState, // (partial | (prev) => partial) => void
subscribe, // (listener) => unsubscribe
};
}
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
setState({ count: 1 }) keeps the other keys. The updater form (prev) => partial computes the patch from the current state.prevState stays a valid snapshot.listener(nextState, prevState).