# A Minimal Implementation of Global Store in React

> A minimal global state management library inspired by Zustand, built to understand how state management works under the hood.

Source: https://www.harxit.com/experiments/react-state-management

- Author: [Harshit Sharma](https://www.harxit.com/about)
- Published: March 1, 2025
- Technologies: TypeScript, React
- Repository: [GitHub](https://github.com/C-W-D-Harshit/react-minimal-state-management)

I wanted to understand how libraries like Zustand actually work. Not just the API, but the internals. So I built one from scratch.

## The core: a pub-sub store

The entire state management system is built on a simple pub-sub pattern. A `createStore` function holds state in a closure, maintains a `Set` of listeners, and notifies them on every `setState` call.

```ts
function createStore<T>(createState: StateCreator<T>): StoreApi<T> {
  let state: T;
  const listeners = new Set<Listener>();

  const setState = (partial) => {
    const nextState =
      typeof partial === "function" ? partial(state) : partial;

    state = Object.assign({}, state, nextState);
    listeners.forEach((listener) => listener());
  };

  const getState = () => state;

  const subscribe = (listener: Listener) => {
    listeners.add(listener);
    return () => listeners.delete(listener);
  };

  const api = { setState, getState, subscribe, destroy };
  state = createState(setState, getState, api);
  return api;
}
```

That's it. No framework magic. Just a closure, a Set, and Object.assign.

## Connecting to React

The tricky part is making React re-render only when the selected state changes. The hook subscribes to the store on mount, runs a selector on every state change, and only calls `setState` if the selected value actually changed.

```ts
const useStore = <U>(
  selector = (state) => state,
  equalityFn = Object.is
) => {
  const [selectedState, setSelectedState] = useState(() =>
    selector(api.getState())
  );
  const selectorRef = useRef(selector);
  const equalityFnRef = useRef(equalityFn);
  const selectedStateRef = useRef(selectedState);

  useEffect(() => {
    const listener = () => {
      const next = selectorRef.current(api.getState());
      if (!equalityFnRef.current(selectedStateRef.current, next)) {
        selectedStateRef.current = next;
        setSelectedState(next);
      }
    };
    return api.subscribe(listener);
  }, []);

  return selectedState;
};
```

The refs are critical here. Without them, the effect would need the selector and equality function as dependencies, causing resubscription on every render.

## Selector optimization

This is where it gets interesting. Each component can subscribe to a different slice of state:

```tsx
// Only re-renders when count changes
const count = useStore((state) => state.count);

// Only re-renders when the filtered list changes
const activeTodos = useStore((state) =>
  state.todos.filter((t) => !t.completed)
);
```

For object selections, a shallow comparison prevents unnecessary re-renders:

```ts
function shallow<T>(objA: T, objB: T): boolean {
  if (Object.is(objA, objB)) return true;
  if (typeof objA !== "object" || typeof objB !== "object") return false;

  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);
  if (keysA.length !== keysB.length) return false;

  for (const key of keysA) {
    if (!Object.is(objA[key], objB[key])) return false;
  }
  return true;
}
```

## The API it produces

The `create` function returns a hook with store methods attached directly to it:

```tsx
const useCounterStore = create<CounterState>(() => ({
  count: 0,
}));

// In a component
const count = useCounterStore((state) => state.count);

// Outside a component
useCounterStore.setState({ count: 5 });
useCounterStore.getState().count;
```

This pattern of attaching `setState`, `getState`, and `subscribe` directly to the hook is exactly what Zustand does. It means you can use the store anywhere, not just inside React components.

## What I took away from this

- useSyncExternalStore exists for a reason. My implementation uses useState + useEffect, which can cause tearing in concurrent mode. Zustand v4+ uses useSyncExternalStore to avoid this. That was the biggest gap I found.
- Selectors are the performance story. Without selectors, every component subscribing to the store re-renders on every state change. The selector + equality check pattern is what makes fine-grained subscriptions work.
- Refs solve the stale closure problem. The listener runs in a useEffect closure, but needs access to the latest selector and equality function. Refs bridge that gap.
- The pub-sub pattern is everywhere. Once you see it here, you start recognizing it in event emitters, Redux, MobX, and even the DOM event system.

The whole library is ~120 lines of TypeScript. For production, use Zustand. But building this gave me a mental model I couldn't have gotten from reading docs.
