Monday, 22 March 2021

React: is this a good way to implement a shared state subscription?

Not sure if this is a so-called "pub/sub" pattern or a form of a "pub/sub" pattern. I am trying to create a piece of shared state so that different components can subscribe to it and only gets updated when there is an update with that state.


const useForceUpdate = () => useReducer((state) => !state, false)[1];

const createSharedState = (reducer, initialState) => {
  const subscribers = [];
  let state = initialState;
  const dispatch = (action) => {
    state = reducer(state, action);
    subscribers.forEach((callback) => callback());
  };
  const useSharedState = () => {
    const forceUpdate = useForceUpdate();
    useEffect(() => {
      const callback = () => forceUpdate();
      subscribers.push(callback);
      const cleanup = () => {
        const index = subscribers.indexOf(callback);
        subscribers.splice(index, 1);
      };
      return cleanup;
    }, []);
    return [state, dispatch];
  };
  return useSharedState;
};

const initialState = 0;
const reducer = (state, action) => {
  switch (action.type) {
    case "increment":
      return state + 1;
    case "decrement":
      return state - 1;
    case "set":
      return action.count;
    default:
      return state;
  }
};

const useCount1 = createSharedState(reducer, initialState);
const useCount2 = createSharedState(reducer, initialState);

const Counter = ({ count, dispatch }) => (
  <div>
    {count}
    <button onClick={() => dispatch({ type: "increment" })}>+1</button>
    <button onClick={() => dispatch({ type: "decrement" })}>-1</button>
    <button onClick={() => dispatch({ type: "set", count: 0 })}>reset</button>
  </div>
);

const Counter1 = () => {
  const [count, dispatch] = useCount1();
  return <Counter count={count} dispatch={dispatch} />;
};

const Counter2 = () => {
  const [count, dispatch] = useCount2();
  return <Counter count={count} dispatch={dispatch} />;
};

const Example = () => (
  <>
    <Counter1 />
    <Counter1 />
    <Counter2 />
    <Counter2 />
  </>
);

Here is a live demo: https://codesandbox.io/s/pubsub-q716s?file=/src/App.js

It seems to be working fine. My questions are:

  1. Is this a valid way to implement shared update subscription?
  2. Is there any drawbacks with using a simple variable to hold the state + forcing React to re-render if that piece of state changes, instead of using useState or useReducer as one normally would do?
  3. any feedback is welcomed.


from React: is this a good way to implement a shared state subscription?

No comments:

Post a Comment