DEV Community

jealous
jealous

Posted on

Share the state of a hook between components

I'm going to show you how to share a hook's state between components.

Example

We have three components: Label, Increment, and Decrement. The goal is for the count in the Label to update when either the Increment or Decrement button is clicked.
A live example of following code can be found here:
https://codesandbox.io/p/sandbox/hook-sample-w9rlht

// App.tsx
root.render(
  <>
    <App />
  </>
);

// index.tsx
const App = () => {
  return (
    <>
      <Label />
      <Increment />
      <Decrement />
    </>
  );
};

// useCounter.ts
export default function useCounter() {
  const [count, setCount] = useState(0);
  const decrease = () => setCount(count - 1);
  const increase = () => setCount(count + 1);
  return {
    count,
    decrease,
    increase,
  };
}

// label.tsx
export default function Label() {
  const counter = useCounter();
  return (
      <div>Count: {counter.count}</div>
  );
}

// increment.tsx
export default function Increment() {
  const counter = useCounter();
  return (
      <button onClick={counter.increase}>Increment</button>
  );
}

// decrement.tsx
export default function Decrement() {
  const counter = useCounter();
  return (
      <button onClick={counter.decrease}>Decrement</button>
  );
}
Enter fullscreen mode Exit fullscreen mode

If you run the example from the link above, you'll see that this code doesn't work. This is because a custom hook is designed to share logic, not state directly.

Share a hook's state globally

Let's use https://github.com/houpjs/houp to share useCounter hook's state globally.
A live example of following code can be found here:
https://codesandbox.io/p/sandbox/share-hook-state-across-components-rfq6j3

// provider.ts
export const Provider = createProvider([useCounter]);

// App.tsx
root.render(
  <Provider>
    <App />
  </Provider>
);

// index.tsx
const App = () => {
  return (
    <>
      <Label />
      <Increment />
      <Decrement />
    </>
  );
};

// useCounter.ts
export default function useCounter() {
  const [count, setCount] = useState(0);
  const decrease = () => setCount(count - 1);
  const increase = () => setCount(count + 1);
  return {
    count,
    decrease,
    increase,
  };
}

// label.tsx
export default function Label() {
  const counter = useStore(useCounter);
  return (
      <div>Count: {counter.count}</div>
  );
}

// increment.tsx
export default function Increment() {
  const counter = useStore(useCounter);
  return (
      <button onClick={counter.increase}>Increment</button>
  );
}

// decrement.tsx
export default function Decrement() {
  const counter = useStore(useCounter);
  return (
      <button onClick={counter.decrease}>Decrement</button>
  );
}
Enter fullscreen mode Exit fullscreen mode

In this revised version, we create a Provider and add it at the root of the app. Then, we call useStore(useCounter) to access the global state.
Now, run the example from the link above, and you'll see that this code works like a charm.

Do your career a big favor. Join DEV. (The website you're on right now)

It takes one minute, it's free, and is worth it for your career.

Get started

Community matters

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Immerse yourself in a wealth of knowledge with this piece, supported by the inclusive DEV Community—every developer, no matter where they are in their journey, is invited to contribute to our collective wisdom.

A simple “thank you” goes a long way—express your gratitude below in the comments!

Gathering insights enriches our journey on DEV and fortifies our community ties. Did you find this article valuable? Taking a moment to thank the author can have a significant impact.

Okay