Cannot update a component while rendering a different component

avatar
Borislav Hadzhiev

Last updated: Apr 7, 2024
5 min

banner

# Table of Contents

  1. Cannot update a component while rendering a different component
  2. An alternative to using the useEffect hook is to update the state when an event occurs
  3. Make sure to call your event handlers correctly
  4. Updating the same component in the body of the function is allowed
  5. Using a hacky solution with the setTimeout() method

# Cannot update a component while rendering a different component

The React.js warning "Cannot update a component while rendering a different component" occurs when:

  1. Component A queues an update in component B.
  2. Component B is still rendering.
  3. A race condition occurs.

To resolve the warning, wrap the logic that updates the state in the useEffect hook.

Here is the complete stack trace.

shell
Warning: Cannot update a component (`App`) while rendering a different component (`Child`). To locate the bad setState() call inside `Child`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render

Here is an example of how the issue occurs.

App.js
import {useState} from 'react'; function App() { const [count, setCount] = useState(0); return ( <div> <h2>bobbyhadz.com</h2> <h2>Count: {count}</h2> <Child count={count} setCount={setCount} /> </div> ); } function Child(props) { // ๐Ÿ‘‡๏ธ calling setState() directly in the body of the component if (props.count === 0) { props.setCount(1); } // Warning: Cannot update a component (`App`) while rendering a different component (`Child`). To locate the bad setState() call inside `Child`, follow the stack trace as described in https://reactjs.org/link/setstate-in-render return ( <div> Child component </div> ); } export default App;

cannot update component while rendering different component

The code for this article is available on GitHub
  1. The App component passes the count and setCount props to the Child component.
  2. The Child component calls the setCount() function and updates the state directly in the body of the function.
  3. A race condition is caused.

In the example:

  1. The Child component calls the setCount() function, so it tries to update the App component.
  2. The Child component is still being rendered, so a race condition is caused.

To resolve the issue, call the setState() method in the useEffect() hook.

App.js
import {useEffect, useState} from 'react'; function App() { const [count, setCount] = useState(0); return ( <div> <h2>bobbyhadz.com</h2> <h2>Count: {count}</h2> <Child count={count} setCount={setCount} /> </div> ); } function Child(props) { // ๐Ÿ‘‡๏ธ Calling setState() directly in the body of the component useEffect(() => { if (props.count === 0) { props.setCount(1); } }, [props]); return ( <div> Child component </div> ); } export default App;

warning is resolved

The code for this article is available on GitHub

The first argument we passed to the useEffect hook is a function that is run when the component mounts.

The second argument is a dependencies array.

index.js
useEffect(() => { if (props.count === 0) { props.setCount(1); } }, [props]);

When one of the dependencies changes, the useEffect hook is rerun.

If you only want to run the useEffect hook when the component mounts, specify an empty dependencies array.

App.js
useEffect(() => { props.setCount(1); }, []);

We only call the setState() function when the component mounts and not in the body of the function component.

If you get the warning that "React Hook useEffect has a missing dependency error", check out the following article.

This way, there aren't any race conditions and the warning is resolved.

If you get the error Too many re-renders. React limits the number of renders to prevent an infinite loop, click on the link and follow the instructions.

# An alternative to using the useEffect hook is to update the state when an event occurs

The useEffect hook is run when the component mounts, so it resolves issues around race conditions.

However, you could also set the state when a certain event occurs to avoid getting the warning.

Here is an example that sets the state when a button is clicked.

App.js
import {useState} from 'react'; function App() { const [count, setCount] = useState(0); return ( <div> <h2>bobbyhadz.com</h2> <h2>Count: {count}</h2> <Child count={count} setCount={setCount} /> </div> ); } function Child(props) { const handleClick = () => { props.setCount(prev => prev + 1); }; return ( <div> Child component <button onClick={handleClick}>Click</button> </div> ); } export default App;

using an event handler instead of useeffect

The code for this article is available on GitHub

The state is set when an event is dispatched and not in the body of the function component.

This way we don't get any race conditions.

The Child component isn't trying to update the App component while it's being rendered.

# Make sure to call your event handlers correctly

The warning is also shown if you call an event handler function immediately as the page loads and not when the event is dispatched.

Here is an example.

App.js
import {useState, useEffect} from 'react'; function App() { const [count, setCount] = useState(0); useEffect(() => { if (count === 0) { setCount(1); } }, [count]); return ( <div> <h2>bobbyhadz.com</h2> <h2>Count: {count}</h2> {/* โ›”๏ธ Don't call the event handler function */} <button onClick={setCount(count + 100)}>Click</button> </div> ); } export default App;
The code for this article is available on GitHub

The issue occurs in the event handler of the button component.

App.js
<button onClick={setCount(count + 100)}>Click</button>

We called the setState() function immediately after the page loads which caused the error.

Instead, the onClick event should be set to a function that is run every time the user clicks the button.

App.js
import {useState, useEffect} from 'react'; function App() { const [count, setCount] = useState(0); useEffect(() => { if (count === 0) { setCount(1); } }, [count]); return ( <div> <h2>bobbyhadz.com</h2> <h2>Count: {count}</h2> <button onClick={() => setCount(count + 100)}> Click </button> </div> ); } export default App;

using an arrow function instead

This time, we set the onClick prop to a function and not to the result of calling one.

App.js
<button onClick={() => setCount(count + 100)}> Click </button>

# Updating the same component in the body of the function is allowed

Calling the setState() method to update the state of the current component is allowed as long as it doesn't cause an infinite re-render loop.

For example, the following code is perfectly valid.

App.js
import {useState} from 'react'; function App() { const [count, setCount] = useState(0); if (count === 0) { setCount(1); } return ( <div> <h2>bobbyhadz.com</h2> <h2>Count: {count}</h2> </div> ); } export default App;
The code for this article is available on GitHub

The if statement checks if the count variable is not equal to 0 and updates the state.

This is allowed because the App component queues an update in itself, not in a parent component that is still rendering.

# Using a hacky solution with the setTimeout() method

You can also use the setTimeout() method to solve the error in a hacky way.

This approach should only be used when you don't want to use the useEffect hook for some reason.

App.js
import {useState} from 'react'; function App() { const [count, setCount] = useState(0); return ( <div> <h2>bobbyhadz.com</h2> <h2>Count: {count}</h2> <Child count={count} setCount={setCount} /> </div> ); } function Child(props) { // ๐Ÿ‘‡๏ธ Wrap the state update in a call to setTimeout setTimeout(() => { if (props.count === 0) { props.setCount(1); } }, 0); return <div>Child component</div>; } export default App;
The code for this article is available on GitHub

The setTimeout method sets a timer that executes a function after a specified delay.

We set the delay to 0 milliseconds to run the function immediately after the event loop has finished.

Wrapping the call to the setState() method in setTimeout() resolves the issue because no race conditions are caused.

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.

Copyright ยฉ 2024 Borislav Hadzhiev