Skip to content
React logic · Core · about 20 minutes

A tiny store

Implement createStore(reducer, initialState) with getState(), dispatch(action) and subscribe(listener). dispatch computes the next state with the reducer, stores it, and then calls every listener. subscribe returns a function that removes that listener. A listener that unsubscribes itself during a dispatch must not make another listener miss that dispatch. In the tests the reducer is a counter (inc, dec); a listener named A logs [A, state]; A! also unsubscribes itself.

Start from

function createStore(reducer, initialState) {
  // Return { getState, dispatch, subscribe }.
}

Hints

Each hint gives away a little more. Open one only when you need it.

  1. Hint 1

    State and the list of listeners live in the closure.

  2. Hint 2

    In dispatch, update the state before calling listeners, and loop over a copy of the list.

  3. Hint 3

    subscribe returns a function that filters that listener out.

Solution

From here on the page gives the method away. If you are still solving, go back to the hints.

JavaScript
function createStore(reducer, initialState) {
  let state = initialState;
  let listeners = [];
  return {
    getState: () => state,
    dispatch(action) {
      state = reducer(state, action);
      for (const listener of [...listeners]) listener();
      return action;
    },
    subscribe(listener) {
      listeners.push(listener);
      return () => {
        listeners = listeners.filter((item) => item !== listener);
      };
    },
  };
}

What an interviewer looks for

Redux, Zustand and useSyncExternalStore all have this shape. Writing it shows you know what a store is under the library.