Store
Create a Store
import { createStore } from 'floppy-disk';
type CatStore = {
age: number;
isSleeping: boolean;
increaseAge: () => void;
reset: () => void;
};
const useCatStore = createStore<CatStore>(({ set }) => ({
age: 0,
isSleeping: false,
increaseAge: () => set((state) => ({ age: state.age + 1 })),
reset: () => set({ age: 0, isSleeping: false }),
}));
Use the Hook Anywhere
No providers are needed.
function Cat() {
const age = useCatStore('age');
return <div>Cat's age: {age}</div>;
}
function Control() {
const increaseAge = useCatStore('increaseAge');
return <button onClick={increaseAge}>Increase cat's age</button>;
}
Control the Reactivity
The concept is same as useEffect
dependency array.
function Cat() {
const { age, isSleeping } = useCatStore();
// Will re-render every state change ^
}
function Cat() {
const { age, isSleeping } = useCatStore((state) => [state.isSleeping]);
// Will only re-render when isSleeping is updated ^
// Update on age won't cause re-render this component
}
function Cat() {
const { age, isSleeping } = useCatStore((state) => [state.age, state.isSleeping]);
// Will re-render when age or isSleeping is updated ^
}
function Cat() {
const { age, isSleeping } = useCatStore((state) => [state.age > 3]);
// Will only re-render when (age>3) is updated
}
Even simpler way, after version 2.13.0
, we can use store's object key:
function YourComponent() {
const age = useCatStore('age');
// Will only re-render when age is updated
}
function YourComponent() {
const age = useCatStore('isSleeping');
// Will only re-render when isSleeping is updated
}
Set Default Reactivity
const useCatStore = createStore(
({ set }) => ({
age: 0,
isSleeping: false,
increaseAge: () => set((state) => ({ age: state.age + 1 })),
reset: () => set({ age: 0, isSleeping: false }),
}),
{
defaultDeps: (state) => [state.age],
},
);
function Cat() {
const { age } = useCatStore();
// ^will only re-render when age changed
return <div>Cat's age: {age}</div>;
}
Using Store Outside Component
Reading/writing state and reacting to changes outside of components.
const alertCatAge = () => {
alert(useCatStore.get().age);
};
const toggleIsSleeping = () => {
useCatStore.set((state) => ({ isSleeping: !state.isSleeping }));
};
const unsub = useCatStore.subscribe(
// Action
(state) => {
console.log('The value of age is changed!', state.age);
},
// Reactivity dependency (just like useEffect dependency mentioned above)
(state) => [state.age],
// ^If not set, the action will be triggered on every state change
);
const getSubscribersOfCat = () => useCatStore.getSubscribers();
Important Notes
Don't mutate.
import { createStore } from 'floppy-disk';
const useCartStore = createStore(({ set, get }) => ({
products: [],
addProduct: (newProduct) => {
const currentProducts = get().products;
product.push(newProduct); // ❌ Don't mutate
set({ product });
},
}));
Don't use conditional reactivity selector.
function Cat({ isSomething }) {
const value = useCatStore(isSomething ? 'age' : 'isSleeping'); // ❌
const { age } = useCatStore(isSomething ? (state) => [state.age] : null); // ❌
const { age } = useCatStore((state) => (isSomething ? [state.age] : [state.isSleeping])); // ❌
...
}
No need to memoize the reactivity selector.
function Cat() {
const selectAge = useCallback((state) => [state.age], []); // ❌
const { age } = useCatStore(selectAge);
...
}