Videos
I made a Cat component using a minimal zustand store:
import { shallow } from "zustand/shallow";
import { CatStoreSchema, useCatStore } from "./CatStore";
export const Cat = () => {
const selector = (state: CatStoreSchema) => ({
meows: state.meows,
incrementMeows: state.incrementMeows,
});
const { meows, incrementMeows } = useCatStore(selector, shallow);
return (
<div>
<span>Cat meowed {meows} times</span>
<button onClick={incrementMeows}>Meow!</button>
</div>
);
}The Store:
import { create } from 'zustand';
export type CatStoreSchema = {
meows: number;
incrementMeows: () => void;
};
export const useCatStore = create<CatStoreSchema>((set, get) => ({
meows: 0,
incrementMeows: () => set(state => ({ meows: state.meows + 1 })),
})); Now, this works great. But what if I want each component to get their own store? Because this is what happens when rendering multiple cat components in a list:
Cat meowed 15 times [Meow!] Cat meowed 15 times [Meow!]
They share the store. I tried reading about providers but could not get my head around it. How can I ensure unique/separate stores per component here?
UPDATE
I did not mention, in my app <Cat /> will have sub components also needing store data. As it is now, they will select that directly from the store, they are not passed anything. I hope to find a solution where that will continue to work
You have to create the definition of your state separate from when you instantiate it.
interface MyState {
bears: number;
setBears: (value: number) => void;
getBears: () => number;
killBears: () => void;
}
const definition = (
set: (
partial:
| MyState
| Partial<MyState>
| ((state: MyState) => MyState | Partial<MyState>),
replace?: boolean | undefined
) => void,
get: () => MyState
) => ({
bears: 0,
setBears: (value: number) => set({ bears: value }),
getBears: () => get().bears,
killBears: () => set({ bears: 0 }),
} as MyState);
export const useState1 = create<MyState>(definition);
export const useState2 = create<MyState>(definition);
For different independent instances of the same store structure, without using hardcoded store instantiation, you can use React Context + Zustand.
- You need a store creator function:
const createMyStore = () => create(...); - In your context provider, create a store:
export function DefectsSourceProvider({ children, }: { children: ReactNode }) { const storeRef = useRef<MyStore>(); storeRef.current = createMyStore(); return ( <Provider value={storeRef.current}> {children} </Provider> ); } - Inside that provider, use a hook to use that store.
export function useStoreInstance<T>( selector: (state: StateCreators) => T, ): T { const storeInstance = useContext(StoreInstanceContext); return useStoreWithEqualityFn(storeInstance, selector, shallow); } - In your component, you can now use
useStoreInstance(state => state.bears).