Reputation: 466
I'm trying to create some hooks to fetch slices of the Redux store so that the store can't access directly to return anything.
If I useSelector directly in the component it works great and doesn't unnecessarily re-render:
*** Modal.tsx ***
const { isModalOpen } = useSelector((state: RootState) => state.carsSlice); // Works great. No unnecessary re-renders
If I create a hook to return the carsSlice then it unnecessarily re-renders the whole page. Why?
E.g.,
*** StateHooks.ts ***
const useGlobalState = () => useSelector((state: RootState) => state);
export const useCarsState = () => useGlobalState().carsSlice;
*** Modal.tsx ***
const { isModalOpen } = useCarsState(); // Re-renders underlying page and is significantly slower than above approach
I'm fetching the specific value from the state so I don't understand why it would re-render the whole page it is being used on? Is there a way to do this?
I've also tried the below but it still causes page re-render:
const useCarsState = useSelector((state: RootState) => state.carsSlice); // Same result
The only way it works as expected is the below BUT I want the custom hooks above:
const { isModalOpen } = useSelector((state: RootState) => state.carsSlice); // Works great
Thanks all.
Upvotes: 2
Views: 920
Reputation: 67439
useSelector
works by doing reference comparisons of the value you return. If that value changes, it forces the component to re-render.
Because of that, you should never return the entire root state from `useSelector! That will cause the component to always re-render.
It doesn't matter what additional destructuring you do with the returned value later - what matters is what the selector itself returns.
That's why you should always select the smallest piece of state needed by a given component, to ensure that it only re-renders when it absolutely needs to because its data actually changed.
Upvotes: 1
Reputation: 71
you can use useShallowEqualSelector It will do only rerender when the value from the store is changed.
Moreover, you can put this into a separate hook like:
import { TypedUseSelectorHook, useSelector, shallowEqual } from "react-redux";
import { RootState } from "store/rootReducer";
const useShallowEqualSelector: TypedUseSelectorHook<RootState> = (selector) =>
useSelector(selector, shallowEqual);
export default useShallowEqualSelector;
in this case, you are able to use this hook everywhere and you have access to the hints from typescript
Upvotes: 1