Use scoped store
This tutorial will guide you on how to implement a scoped store.
In this tutorial, we'll create a simple Counter
component, which contains two sub components: Label
and Updater
.
The Label
component displays the current count, while the Updater
component provides functionality to update the count.
Setup
Add Houp in you project.
npm install houp
Create useCounter
hook
import { useState } from "react";
export default function useCounter() {
const [count, setCount] = useState(0);
return {
count,
setCount,
};
}
Create a Provider and add it to your app
import useCounter from "./useCounter";
import { createProvider } from "houp";
export const Provider = createProvider([useCounter]);
import { StrictMode } from "react"
import { createRoot } from "react-dom/client"
import App from "./App"
import { Provider } from "./provider";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<Provider>
<App />
</Provider>
</StrictMode>,
)
Create the Counter component
The Updater
component has two buttons: increase
and decrease
.
The increase
button increases the count by 1, and the decrease
button decreases the count by 1.
import { useStore } from "houp";
import useCounter from "./useCounter";
function Label() {
const counter = useStore(useCounter);
return (
<>
<div>{counter.count}</div>
</>
);
}
function Updater() {
const counter = useStore(useCounter);
return (
<>
<button onClick={() => counter.setCount(n => n + 1)}>increase</button>
<button onClick={() => counter.setCount(n => n - 1)}>decrease</button>
</>
);
}
export default function Counter() {
return (
<>
<Label />
<Updater />
</>
);
}
The number will be changed when we click the buttons, and it works as expected.
Currently, we have added only one Counter
component.
What happens if we add multiple Counter
components? Let's add two Counter
components and see what happens when we click the buttons.
You've probably noticed the problem: every time you click the button to change the number,
the number in the other Counter
component also changes.
This happens because we are using useStore(useCounter)
to manage state.
What this does is find the nearest StoreProvider
in the component tree that contains the specified hook and returns the hook's state from that provider.
Currently, the nearest StoreProvider
is the root provider, meaning the state is shared across the entire application.
However, this isn't what we want. We want the state of each Counter
component to be independent of the others.
Add a provider to create a scoped store
Now, let's modify the Counter
component to use the useCount
store as a scoped store.
import { useStore } from "houp";
import { createProvider, useStore } from "houp";
import { useCounter } from "./useCounter";
const Provider = createProvider([useCounter]);
function Label() {
const counter = useStore(useCounter);
return (
<>
<div>{counter.count}</div>
</>
);
}
function Updater() {
const counter = useStore(useCounter);
return (
<>
<button onClick={() => counter.setCount(n => n + 1)}>increase</button>
<button onClick={() => counter.setCount(n => n - 1)}>decrease</button>
</>
);
}
export default function Counter() {
return (
<>
<Provider>
<Label />
<Updater />
</>
</Provider>
);
}
createProvider
function creates a StoreProvider
component with an array of hooks as its parameter,
which will be used in the Counter
component. We use the StoreProvider
component as the root of the Counter
component.
Now, click the button to see if the two Counter
components still affect each other.
Full Example
Here's the complete example, running on CodeSandbox.