Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

useReducer: dispatch causes rerender even if state is not changed

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?

like image 459
Michał Kaczanowicz Avatar asked Oct 01 '19 15:10

Michał Kaczanowicz


People also ask

How to update the state of a usereducer?

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.

Does usereducer dispatch have a stable identity?

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".

Does dispatching from a component always cause react to re-render?

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.

Does this component re-render on every state change?

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.


1 Answers

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]);
like image 68
James Avatar answered Oct 21 '22 19:10

James