Looking at the multitude of React state management libs like Redux, Zustand, Jotai, MobX, etc. (and using some of them), I was wondering: Can't we come up with something as straightforward and single-purpose as React's useState()
for something as basic and nearly ubiquitous as shared state?
Most libs, while being working solutions, may seem a bit overloaded for the task of providing shared state. So I created another one to address my question.
It's called Groundstate, and that's how it works. We'll move a portion of a component's state to a shared location just by replacing useState()
with another hook and by wrapping the state value with the Store
class:
+ import {Store, useStore} from 'groundstate';
+ let AppContext = createContext(new Store(0));
let Counter = () => {
- let [value, setValue] = useState(0);
+ let [value, setValue] = useStore(useContext(AppContext));
let handleClick = useCallback(() => {
setValue(value => value + 1);
}, [setValue]);
return <button onClick={handleClick}>{value}</button>;
};
Before the changes, the counter value used to be visible only to the component itself. After the changes, the counter value can be accessed and changed from outside of the Counter
component. In the example above, we've got a primitive value in the store, but it can be any type.
The point is this shared state setup introduces as few intermediaries as possible, and the interaction with the shared state remains the same as with local state from useState()
. Which means the shared state setup is nearly effortless, also allowing for quick migration from local state to shared state and the other way around.
Optionally, besides sharing the state, moving the state out of the component in fact can also be used as persistent local state.
More details
As we saw in the example above, all what's needed to set up shared state is the useStore()
hook and the Store
class.
The Store
class provides methods to access and update the state value. The useStore()
hook subscribes the component to the store state changes and unpacks the state value from the store.
The reference to the instance of the Store
class passed to the Context (initialized as new Store(0)
in the example above) doesn't change when we update its state via setValue()
: the store state changes, the store instance does not. Which means that updating the state doesn't trigger re-renders in the entire DOM tree nested into the Context Provider by default, affecting only the components that explicitly subscribed to the store via the useStore()
hook. Besides, the useStore()
hook also offers an option to fine-tune the component's responsiveness to store updates, when it's necessary.
And that's, in essence, what makes up the Groundstate's minimalist single-purpose approach to shared state management that I've been looking for.
Top comments (0)