Best practices on using selectors in v5 #2867
-
We just completed the migration to Zustand v5 and we would like to share this journey with you. Right after bumping the Zustand version to v5.0.1, our React-Native app wouldn't start because of this error: This error was being thrown from many places in our app so we started to fix them one by one. In order to fix the way we were using our selectors, we divided the selectors into 3 categories. 1. Simple selectors (no useShallow needed)These are the selectors that return primitive values e.g. number, boolean, string const selectProp = (state: State) => state.prop;
const prop = useStore(selectProp); 2. Selectors that lightly transform the state (useShallow needed!)These are the selectors that select something from the state and the output has a new reference each time it is called. const selectPetNames = (state: State) => state.pets.map(pet => pet.name); // new array is returned each time
const petNames = useStore(useShallow(selectPetNames)); 3. Selectors with nested objects (useSwallow doesn't work here)This was the most tricky category because the selectors in this category return an output that has a new reference each time it is called and also it contains nested objects/arrays that also have new reference each time they are called. const selectPetWithExtraInfo = (state: State) =>
state.pets.map(pet => {
let extraInfo = {};
if (pet.type === "dog") {
extraInfo = { canRun: true }
}
return { ...pet, extraInfo }; // extraInfo is nested and has a new reference each time
});
const petWithExtraInfo = useStore(useShallow(selectPetWithExtraInfo)); // !! Throws maximum depth error !! In order to fix this we introduced a new hook called import { useDeep } from "src/hooks/useDeep.ts"
const petWithExtraInfo = useStore(useDeep(selectPetWithExtraInfo)); Code Sandbox ExampleYou can find an example of these selectors here Feedback on the approach we followed above is more that welcome! |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
@nkleidis instead of stable selector we need to talk about stable selector results, you could have a stable selector that does not return stable results // Case 1: stable selector result
const prop = useStore(state => state.prop);
// Case 2: stable selector result
const pets = useStore(state => state.pets); // We can use `useShallow` like this `useStore(useShallow(state => state.pets))`
const petNames = pets.map(pet => pet.name)
// Note: you can put this in a custom hook called `usePetNames`
// Case 3: stable selector result
const pets = useStore(state => state.pets); // We can use `useShallow` like this `useStore(useShallow(state => state.pets))`
const petsWithExtraInfo = pets.map(pet => { ... })
// Note: you can put this in a custom hook called `usePetsWithExtraInfo` |
Beta Was this translation helpful? Give feedback.
@nkleidis instead of stable selector we need to talk about stable selector results, you could have a stable selector that does not return stable results