There are many cases where you want to share state between components in React. A lot of popular state management libraries implement this ‘module state’ for you such as Zustand.
We will implement a naive version of a createStore
function that allows you to share your state between components in React.
createStore
has a few functions that we return from it: getState
, setState
, and subscribe
. Just like with React Hooks, we can get and set state within components. Because this is shared state, we need to give components a mechanism for updating when state changes in a separate component, that is where subscribe
comes in. With the combination of these 3 functions, we can display and update a shared value between multiple components in React.
Instructor: [0:00] What we have here is a module state. It's typically exported from a module. In this code, it's just a file scope variable. For simplicity, the module states an object with one property count. Counter one is a component to show the count in the module state.
[0:20] It has a button to increment the count. If you click the button, it will increment the module state and local state. So far, it seems to work, but what will happen if we add a new component counter two? If you click two buttons, they behave really.
[0:40] The display counts are out of sync. To fix this naively, we define a set called setState at module level. This sets towards setState functions. In counter one component, useEffect is used to either setState function to the setState set and remove it on clean up.
[1:02] When operating a module state, we invoke all setState functions in the setState set instead of invoking the local one. We copy counter one to counter two and check the behavior. Now it works as expected, both counts are in sync.
[1:23] Let's make the module state code more usable. We define create store that holds a module state in a closure. In the closure, we define two functions, getState and setState. Finally, return an object containing those functions. Create a new store with an initial state.
[1:48] Now, modify counter one to use the store naively. We use the store.getState and store.setState. For now, we still update local state only for the component. We again copy counter one to counter two and check the behavior.
[2:10] Now it behaves like the first time, counts are out of sync. To fix this, we add subscription mechanism in the store. In create store, listener set is added. Subscribe function is to add the listener set and return a function to delete from it.
[2:31] In counter one component, we again use useEffect to subscribe to the store. A callback function will update the local state. Subscribe returns an unsubscribe function and it's just returned. There's a little issue here, but ignore it for now. We don't need to update local state because it's handling the callback function. Now, is this enough? Copy to counter to and see the behavior. No, it does not work.
[3:08] We need to invoke the listeners in the store. Every time setState is called, all listeners should be invoked. Now it works fine.
[3:22] Let's recap what's done. Create store is a reusable function to define a module state with some functions. It has listeners to notify state updates. Creating a new store is just calling create state with initial state. To use this tool we subscribe in useEffect and on callback, local state is updated. Calls to a large state to update the module state. By this way, multiple components can share the value in the module state.