useReducer: dispatch causes rerender even if state is not changed

Viewed 6005

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?

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]);
Related