Frontend Development 9 min read

Master Global State Management in React with Custom Hooks

Learn how to build a simple global state management solution in React using custom hooks, starting from a basic counter component, extending it with initialization props, synchronizing multiple counters, and finally creating a reusable createGlobalState utility for shared state across components.

KooFE Frontend Team
KooFE Frontend Team
KooFE Frontend Team
Master Global State Management in React with Custom Hooks

Example of developing a counter component, introducing a method to implement global state management using React Hooks.

Simple Counter Component

Below is a simple counter component named

Counter

that starts counting from 0 and increments by one on each click:

<code>const Counter = ({ text }) => {
  const [count, setCount] = useState(0);
  const addCount = () => setCount(count + 1);

  return (
    <div onClick={addCount}>
      {text}: {count}
    </div>
  );
};
</code>

We can further abstract the above

Counter

using a custom hook

useCounter

to implement the counting logic:

<code>const useCounter = (initCount) => {
  const [count, setCount] = useState(initCount || 0);
  const addCount = () => setCount(count + 1);

  return [count, addCount];
};

const Counter = ({ text }) => {
  // Use custom hook useCounter
  const [count, addCount] = useCounter(0);
  return (
    <div onClick={addCount}>
      {text}: {count}
    </div>
  );
};
</code>

Both implementations produce the same functionality. We can render two counters using the

Counter

component:

<code>const APP = () => {
  return (
    <div>
      <Counter text="计数器1" />
      <Counter text="计数器2" />
    </div>
  );
};
</code>

Result:

Counter with Initial Value

The two counters are independent. By adding an

initCount

prop to

Counter

, we can set the initial value:

<code>const useCounter = (initCount) => {
  const [count, setCount] = useState(initCount || 0);

  // Component updates its own count
  const addCount = () => setCount(count + 1);

  // Reset count when initCount changes
  useEffect(() => {
    setCount(initCount);
  }, [initCount]);

  return [count, addCount];
};

const Counter = ({ initCount, text }) => {
  const [count, addCount] = useCounter(initCount);
  return (
    <div onClick={addCount}>
      {text}: {count}
    </div>
  );
};

const APP = () => {
  return (
    <div>
      <Counter initCount={1} text="计数器1" />
      <Counter initCount={0} text="计数器2" />
    </div>
  );
};
</code>

Synchronized Counters

To make two counters update synchronously, share the count state in the parent component and pass both the count and a change handler to each counter via an

onChange

prop:

<code>const useCounter = (initCount, onChange) => {
  const [count, setCount] = useState(initCount || 0);
  const addCount = onChange || (() => setCount(count + 1));

  useEffect(() => {
    setCount(initCount);
  }, [initCount]);

  return [count, addCount];
};

const Counter = ({ initCount, onChange, text }) => {
  const [count, addCount] = useCounter(initCount, onChange);
  return (
    <div onClick={addCount}>
      {text}: {count}
    </div>
  );
};

const APP = () => {
  const [count, setCount] = useState(0);
  const addCount = () => setCount(count + 1);
  return (
    <div className="App">
      <Counter initCount={count} text="计数器1" onChange={addCount} />
      <Counter initCount={count} text="计数器2" onChange={addCount} />
    </div>
  );
};
</code>

Result:

We can further simplify by using a single custom hook for global state:

<code>const APP = () => {
  const [count, addCount] = useCounter(0);

  return (
    <div className="App">
      <Counter initCount={count} text="计数器1" onChange={addCount} />
      <Counter initCount={count} text="计数器2" onChange={addCount} />
    </div>
  );
};
</code>

More General Global State Management

Upgrade the code to a reusable

createGlobalState

utility for simple global state management:

<code>const createGlobalState = (initialState) => {
  let globalState = initialState;
  const listeners = new Set();

  const setGlobalState = (nextGlobalState) => {
    globalState = nextGlobalState;
    listeners.forEach((listener) => listener());
  };

  const useGlobalState = () => {
    const [state, setState] = useState(globalState);
    useEffect(() => {
      const listener = () => setState(globalState);
      listeners.add(listener);
      listener();
      return () => listeners.delete(listener);
    }, []);
    return [state, setGlobalState];
  };

  return {
    setGlobalState,
    useGlobalState,
  };
};
</code>

Using

createGlobalState

for state management:

<code>const { useGlobalState } = createGlobalState(0);

const Counter = ({ text }) => {
  const [state, setGlobalState] = useGlobalState();
  return (
    <div onClick={() => setGlobalState(state + 1)}>
      {text}: {state}
    </div>
  );
};

const App = () => {
  const [state] = useGlobalState();
  return (
    <div className="App">
      <Counter text="计数器" />
      <div>点击次数:{state}</div>
    </div>
  );
};
</code>

Result:

This simple implementation is limited; for a more complete solution, see the

react-hooks-global-state

library, which inspired this article.

frontend developmentReactHooksGlobal StateCustom Hook
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.