I noticed that if I dispatch an action that happens to not to modify the state, the component is re-rendered anyway.
Example:
// for simplicity sake, we simply pass state on (no mutation)
const someReducer = (state, action) => state
const App = () => {
const [state, dispatch] = useReducer(someReducer, 0)
// in my use case, I want to dispatch an action if some specific condition in state occurs
if(state === 0) {
dispatch({ type: 'SOME ACTION' }) // type doesn't matter
}
// return some JSX
}
I get:
Error in app.js (16929:26)
Too many re-renders. React limits the number of renders to prevent an infinite loop.
Is this by design? Should it be this way?
useReducer returns an array, whose first item represent current state and other one is dispatch function. const [state, dispatch] = useReducer(reducer, initialState); // state and dispatch is just a naming convention. We can update the state by calling dispatch method.
According to useReducer docs, dispatch is has a stable identity. I interpreted this to mean that when a component extracts dispatch from a useReducer, it won't re-render when the state connected to that dispatch changes, because the reference to dispatch will always be the same. Basically, dispatch can "treated like a static value".
The second issue is that yes, dispatching while always cause React to queue a state update and try calling the reducer, and if the reducer returns a new value, React will continue re-rendering. Doesn't matter what component you've dispatched from - causing state updates and re-rendering is the point of useReducer in the first place.
But according to the rules of hooks, a component like this would re-render on every state change even though it doesn't subscribe to any of the state it mutates. Of course I could use something like useMemo to control this components re-rendering rules, but still.
In terms of your example as-is, it's not immediately obvious as to what is causing the component to re-render. However, the docs seem to suggest that it's not a guarantee that a re-render won't occur when you don't mutate the state:
Note that React may still need to render that specific component again before bailing out. That shouldn’t be a concern because React won’t unnecessarily go “deeper” into the tree. If you’re doing expensive calculations while rendering, you can optimize them with useMemo.
This is why it's generally recommended that you run code that potentially has side effects in useEffect
e.g.
const [state, dispatch] = useReducer(someReducer, 0);
...
useEffect(() => {
if (state === 0) {
dispatch({ type: 'SOME ACTION' });
}
}, [state]);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With