Reusable is a solution for state management, based on React Hooks. It allows you to transform your custom hooks to singleton stores, and subscribe to them directly from any component.
Pass your custom hooks to createStore
:
const useCounter = createStore(() => {
const [counter, setCounter] = useState(0);
return {
counter,
increment: () => setCounter(prev => prev + 1)
}
});
and get a singleton store, with a hook that subscribes directly to that store:
const Comp1 = () => {
const something = useCounter();
}
const Comp2 = () => {
const something = useCounter(); // same something
}
Then wrap your app with a provider:
const App = () => (
<ReusableProvider> {/* no initialization code, stores automatically plug into the top provider */}
...
</ReusableProvider>
)
You can also use selectors, and your component will re-render only if the return value changed:
const Comp1 = () => {
const isPositive = useCounter(state => state.counter > 0);
// Will only re-render if switching between positive and negative
}
Every store can use any other store, without worrying about provider order. Just use the store's hook inside the other store:
const useCurrentUser = createStore(() => ...);
const usePosts = createStore(() => ...);
const useCurrentUserPosts = createStore(() => {
const currentUser = useCurrentUser();
const posts = usePosts();
return ...
});
- Use hooks to manage global state, just like you manage local state
- Use any custom hooks you already have inside the global state management
- Stores won't rely on keys, giving better reusability
- Stores are decoupled from component tree
- You can use selectors and bail out of render
- Stores can co-depend without worrying about provider order
- It's easy to create packages that rely on global state, and plug them into apps seamlessly
Most current state management solutions don't let you manage state using hooks, which causes you to manage local and global state differently, and have a costly transition between the two.
Reusable solves this by seemingly transforming your custom hooks into global stores.
Sounds like an anti-pattern, but in fact decoupling your state management solution from your component tree gives the developers a lot of flexibilty while designing both the component tree, and the app's state.
Using plain context has some drawbacks and limitations, that led us to write this library:
- Context doesn't support selectors, render bailout, or debouncing
- When managing global state using Context in a large app, you will probably have many small, single-purpose providers. Soon enough you'll find a Provider wrapper hell.
- When you order the providers vertically, you can’t dynamically choose to depend on each other without changing the order, which might break things.
React hooks must run inside a component, and our store is based on a custom hook.
So in order to have a store that uses a custom hook, we need to create a component for each of our stores.
The ReusableProvider
component renders a Stores
component, under which it will render one component per store, which only runs the store's hook, and renders nothing to the DOM. Then, it uses an effect to update all subscribers with the new value.
Notice that the ReusableProvider
uses a Context provider at the top-level, but it provides a stable ref that never changes. This means that changing store values, and even dynamically adding stores won't re-render your app.
Check out the docs here: https://reusablejs.github.io/reusable/docs/basic-usage.html
We would love your feedback / suggestions Please open an issue for discussion before submitting a PR Thanks