FossilsðŸ’ŧ CodingImplement Observable / Reactive Store
ðŸĶ–DinosaurJavaScriptPatternsState Management

Implement Observable / Reactive Store

Build a reactive state container from scratch. This tests your understanding of the observer pattern, proxies, and how state management libraries work internally.

Implement Observable / Reactive Store

Interview Question: "Implement a simple reactive store where state changes automatically notify subscribers."

Level 1: Basic Observable Store

function createStore(initialState) {
  let state = initialState;
  const listeners = new Set();
 
  return {
    getState: () => state,
 
    setState: (updater) => {
      const nextState = typeof updater === 'function'
        ? updater(state)
        : { ...state, ...updater };
 
      if (nextState !== state) {
        state = nextState;
        listeners.forEach(fn => fn(state));
      }
    },
 
    subscribe: (listener) => {
      listeners.add(listener);
      return () => listeners.delete(listener);
    },
  };
}

Level 2: With Selectors (Zustand-like)

function createStore(initializer) {
  let state;
  const listeners = new Set();
 
  const get = () => state;
 
  const set = (partial) => {
    const nextState = typeof partial === 'function' ? partial(state) : partial;
    if (Object.is(nextState, state)) return;
 
    const previousState = state;
    state = typeof nextState === 'object'
      ? { ...state, ...nextState }
      : nextState;
 
    listeners.forEach(fn => fn(state, previousState));
  };
 
  state = initializer(set, get);
 
  function subscribe(selector, callback) {
    if (!callback) {
      callback = selector;
      selector = (s) => s;
    }
 
    let currentSlice = selector(state);
 
    const listener = (nextState) => {
      const nextSlice = selector(nextState);
      if (!Object.is(currentSlice, nextSlice)) {
        const prev = currentSlice;
        currentSlice = nextSlice;
        callback(currentSlice, prev);
      }
    };
 
    listeners.add(listener);
    return () => listeners.delete(listener);
  }
 
  return { getState: get, setState: set, subscribe };
}
 
// Usage
const store = createStore((set, get) => ({
  count: 0,
  users: [],
  increment: () => set({ count: get().count + 1 }),
  addUser: (user) => set({ users: [...get().users, user] }),
}));
 
store.subscribe(
  (s) => s.count,
  (count) => console.log('Count changed:', count)
);
 
store.getState().increment(); // "Count changed: 1"
store.getState().addUser({ name: 'Alice' }); // (no log — count didn't change)

Key points:

  • Selector-based subscriptions — Only notifies when the selected slice changes
  • Object.is comparison — Same identity check React uses
  • Functional updates — set accepts both objects and updater functions
  • Initializer pattern — set and get are passed to the state factory (Zustand pattern)

Level 3: Proxy-Based Reactivity (Vue-like)

function reactive(target, onChange) {
  const handler = {
    get(obj, prop) {
      const value = obj[prop];
      if (typeof value === 'object' && value !== null) {
        return reactive(value, onChange);
      }
      return value;
    },
 
    set(obj, prop, value) {
      const oldValue = obj[prop];
      if (Object.is(oldValue, value)) return true;
 
      obj[prop] = value;
      onChange(prop, value, oldValue);
      return true;
    },
  };
 
  return new Proxy(target, handler);
}
 
// Usage
const state = reactive({ count: 0, user: { name: 'Alice' } }, (prop, newVal) => {
  console.log(`${String(prop)} changed to ${newVal}`);
});
 
state.count = 1;             // "count changed to 1"
state.user.name = 'Bob';    // "name changed to Bob"

Follow-Up: "How does this compare to Redux/Zustand?"

"Redux uses action dispatching with reducers — explicit updates with full action history. Zustand simplifies this with direct mutations and selector-based subscriptions. My implementation is closest to Zustand's model. The key architectural difference is that Proxy-based reactivity (Vue) tracks access automatically, while selector-based (Zustand/Redux) requires explicit subscription declarations."