Frontend Development 7 min read

Build a Simple React Global State Manager Using ES6 Proxy

This article demonstrates how to leverage the ES6 Proxy object to create a lightweight global state management solution for React, walking through a counter example, custom hooks, listener tracking, and a reusable store creator for seamless component updates.

KooFE Frontend Team
KooFE Frontend Team
KooFE Frontend Team
Build a Simple React Global State Manager Using ES6 Proxy

Proxy Overview

Proxy is an ES6 feature that places an interception layer in front of a target object, forcing all external accesses to pass through it. This enables filtering or rewriting of operations. The basic syntax is:

<code>const p = new Proxy(target, handler)</code>

The two parameters are:

target – the object to be wrapped (any type, including arrays, functions, or another proxy).

handler – an object whose functions define the proxy’s behavior for various operations.

Example:

<code>const state = { count: 0 };
const store = new Proxy(state, {
  set(target, propertyKey, value, receiver) {
    Reflect.set(target, propertyKey, value, receiver);
    console.log(propertyKey, value);
    return true;
  },
  get(target, propertyKey, receiver) {
    return Reflect.get(target, propertyKey, receiver);
  }
});
store.count = 1; // logs "count 1"
console.log(store.count); // 1
console.log(state.count); // 1</code>

Simple State Management

We can wrap the above logic in a custom React Hook that returns the proxy store:

<code>const useProxyStore = (initialValue) => {
  const [state, setState] = useState(initialValue);
  const store = new Proxy(state, {
    set(target, propertyKey, value, receiver) {
      Reflect.set(target, propertyKey, value, receiver);
      setState(Object.assign({}, state, { [propertyKey]: value }));
      return true;
    },
    get(target, propertyKey, receiver) {
      return Reflect.get(target, propertyKey, receiver);
    }
  });
  return store;
};</code>

Using this hook in a counter component:

<code>const Counter = ({ text }) => {
  const store = useProxyStore({ count: 0 });
  return (
    <div onClick={() => { store.count += 1; }}>
      {text}: {store.count}
    </div>
  );
};</code>

Clicking the div increments

store.count

without calling

setState

directly.

Global State Management

To make the state truly global, we need to track which components depend on it and re‑render them when the state changes. A

Set

of listener functions can be used for this purpose:

<code>const listeners = new Set();
const useStore = () => {
  const [, setState] = useState();
  useEffect(() => {
    const fn = () => setState({});
    listeners.add(fn);
    return () => listeners.delete(fn);
  }, []);
};</code>

When the proxy’s

set

trap runs, it notifies all listeners:

<code>const store = new Proxy(state, {
  set(target, propertyKey, value, receiver) {
    Reflect.set(target, propertyKey, value, receiver);
    for (const listener of listeners) {
      listener();
    }
    return true;
  },
  get(target, propertyKey, receiver) {
    return Reflect.get(target, propertyKey, receiver);
  }
});</code>

A reusable creator combines these pieces:

<code>const createGlobalStore = (initialValue) => {
  const listeners = new Set();
  const store = new Proxy(state, {
    set(target, propertyKey, value, receiver) {
      Reflect.set(target, propertyKey, value, receiver);
      for (const listener of listeners) {
        listener();
      }
      return true;
    },
    get(target, propertyKey, receiver) {
      return Reflect.get(target, propertyKey, receiver);
    }
  });
  const useStore = () => {
    const [, setState] = useState();
    useEffect(() => {
      const fn = () => setState({});
      listeners.add(fn);
      return () => listeners.delete(fn);
    }, []);
    return store;
  };
  return [store, useStore];
};</code>

Usage example:

<code>const [store, useStore] = createGlobalStore({ count: 0 });
setTimeout(() => { store.count = 100; }, 5000);

const Counter = () => {
  const store = useStore();
  return (
    <div onClick={() => { store.count += 1; }}>
      {store.count}
    </div>
  );
};

const App = () => {
  const store = useStore();
  return (
    <div className="App">
      <Counter />
      {store.count}
    </div>
  );
};</code>

The animation below shows the counter updating both locally and globally.

Thus, by leveraging ES6 Proxy we can implement a lightweight global state management solution for React. For deeper details, refer to the source code of the

@slimlib/store

library.

frontendJavaScriptProxyreactGlobal State
KooFE Frontend Team
Written by

KooFE Frontend Team

Follow the latest frontend updates

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.