How to fix "Cannot update a component while rendering a different component"
React logs a warning in the console:
Warning: Cannot update a component (`Parent`) 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
The UI sometimes gets into an inconsistent state. What is causing this and how do I fix it?
Solution
This warning means that during the render phase of Child, it is calling a state setter that belongs to an ancestor component. React does not allow side effects during rendering.
The most common pattern that causes this is passing a state setter as a prop and calling it inline in the child's render:
// Broken: calling setCount during render of Child
function Child({ onLoad }: { onLoad: () => void }) {
onLoad() // called directly in the render body
return <div>Child</div>
}
function Parent() {
const [count, setCount] = useState(0)
return <Child onLoad={() => setCount((c) => c + 1)} />
}
Move the call to a useEffect inside the child so it runs after rendering, not during it:
function Child({ onLoad }: { onLoad: () => void }) {
useEffect(() => {
onLoad()
}, [onLoad])
return <div>Child</div>
}
Alternative #1
If the state update should happen in response to an event rather than on mount, move the setter call to the event handler instead of the render body:
// Broken: setter called during render
function FilterInput({ onFilterChange }: { onFilterChange: (v: string) => void }) {
onFilterChange(someComputedValue) // runs during render
return <input />
}
// Fixed: setter called on user interaction
function FilterInput({ onFilterChange }: { onFilterChange: (v: string) => void }) {
return (
<input
onChange={(e) => onFilterChange(e.target.value)}
/>
)
}
Alternative #2
Sometimes the root cause is deriving state from props by calling a setter during render as a way to "sync" them. This pattern is unnecessary in React. Compute the derived value directly during render instead:
// Broken: syncing state with props via render-time setter
function ItemList({ items }: { items: string[] }) {
const [filtered, setFiltered] = useState(items)
setFiltered(items.filter((i) => i.length > 3)) // called during render!
return <ul>{filtered.map((i) => <li key={i}>{i}</li>)}</ul>
}
// Fixed: compute derived value directly
function ItemList({ items }: { items: string[] }) {
const filtered = items.filter((i) => i.length > 3) // no state needed
return <ul>{filtered.map((i) => <li key={i}>{i}</li>)}</ul>
}
If the computation is expensive, wrap it with useMemo rather than storing the result in state.