DEV Community

nikita
nikita

Posted on

Share state between React components using custom hooks and observables

React custom hooks are a powerful feature that provide a handy way to organize and reuse logic in a simple functional manner. And mixing them with observables allows us to solve one of the most common problems in a Single Page Apps - state management.

Firstly we need to create some basic observable implementation:

function makeObservable(target) {
  let listeners = []; // initial listeners can be passed an an argument aswell
  let value = target;

  function get() {
    return value;
  }

  function set(newValue) {
    if (value === newValue) return;
    value = newValue;
    listeners.forEach((l) => l(value));
  }

  function subscribe(listenerFunc) {
    listeners.push(listenerFunc);
    return () => unsubscribe(listenerFunc); // will be used inside React.useEffect
  }

  function unsubscribe(listenerFunc) {
    listeners = listeners.filter((l) => l !== listenerFunc);
  }

  return {
    get,
    set,
    subscribe,
  };
}
Enter fullscreen mode Exit fullscreen mode

And then create a store and hook it to React by using subscribe in useEffect:

const userStore = makeObservable({ name: "user", count: 0 });

const useUser = () => {
  const [user, setUser] = React.useState(userStore.get());

  React.useEffect(() => {
    return userStore.subscribe(setUser);
  }, []);

  const actions = React.useMemo(() => {
    return {
      setName: (name) => userStore.set({ ...user, name }),
      incrementCount: () => userStore.set({ ...user, count: user.count + 1 }),
      decrementCount: () => userStore.set({ ...user, count: user.count - 1 }),
    }
  }, [user])

  return {
    state: user,
    actions
  }
}
Enter fullscreen mode Exit fullscreen mode

And that is it! You can now use useUser hook in any of your components, trigger related actions and be sure that state is always up to date. No need for React.Context, lifting state up or using external state management tool.

Let me know what you think and thanks for reading.

Top comments (0)