Last updated: Apr 7, 2024
Reading timeยท5 min
useEffect
hook is to update the state when an event occurssetTimeout()
methodThe React.js warning "Cannot update a component while rendering a different component" occurs when:
To resolve the warning, wrap the logic that updates the state in the useEffect
hook.
Here is the complete stack trace.
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.
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;
App
component passes the count
and setCount
props to the Child
component.Child
component calls the setCount()
function and updates the state
directly in the body of the function.In the example:
Child
component calls the setCount()
function, so it tries to update
the App
component.Child
component is still being rendered, so a race condition is caused.To resolve the issue, call the setState()
method in the
useEffect() hook.
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;
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.
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.
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.
useEffect
hook is to update the state when an event occursThe 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.
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;
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.
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.
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 issue occurs in the event handler of the button component.
<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.
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;
This time, we set the onClick
prop to a function and not to the result of
calling one.
<button onClick={() => setCount(count + 100)}> Click </button>
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.
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 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.
setTimeout()
methodYou 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.
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 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.
You can learn more about the related topics by checking out the following tutorials: