Fix 'cannot update a component while rendering' in React

By

Learn how to fix the cannot update a component while rendering a different component error in React by moving your setState call inside a useEffect hook.

~~~

To fix this error, move the state update out of the render phase and into a useEffect() hook. The error appears when a component calls another component’s setState function while it’s rendering.

Here’s how I ran into it. While working on a React / Next.js application I got this error:

Cannot update a component (`App`) while rendering a different component

I researched a bit how to solve this problem, but there was a lot of confusion in the material I found.

What was I doing wrong?

I had a centralized state managed in the App component:

function MyApp({ Component, pageProps }) {
  const [lessonsRead, setLessonsRead] = useState()

  return (
    <Component
      lessonsRead={lessonsRead}
      setLessonsRead={setLessonsRead}
      {...pageProps}
    />
  )
}

and in a Next.js page component I called setLessonsRead to populate this state with data, based on the result of a SWR (fetch) call:

if (courseData && courseData.lessonsRead) {
  setLessonsRead(courseData.lessonsRead)
}

I was doing this right inside the component body.

That’s the problem. The body of a function component runs during rendering. Calling setLessonsRead() there asks React to update App while it’s still busy rendering the page component. React forbids this: rendering must be a pure calculation, with no side effects like state updates.

The fix

To solve this problem I had to wrap this code in useEffect, to only run it when the data changed and not on every component render:

useEffect(() => {
  if (courseData && courseData.lessonsRead) {
    setLessonsRead(courseData.lessonsRead)
  }
}, [courseData])

Why does useEffect fix it?

useEffect runs after React has finished rendering and committed the result to the DOM. At that point the render phase is over, so updating the App state is allowed. React then schedules a new render with the fresh state. Same outcome, no warning.

The dependency array matters too. With [courseData], the effect only runs when the SWR data changes, not on every render.

Be careful if you omit the array: the effect would run after every render. If the value you set is a new object each time (say, something you build by mapping over the response), each setState triggers a render, which runs the effect again, and you get an infinite render loop. Keep the dependency array, and keep the if guard.

Tagged: React · All topics
~~~

Related posts about react: