Problem Solved

React · cannot update during render

Home › Courses › React › Problems › Cannot update during render
⚛️ Problem · 5 min read

"Cannot update a component while rendering."
Three fixes, one root cause.

You called setState inside render — inside the component body, not inside an event handler. React's warning is precise: renders must be pure. This page shows the three causes, the three fixes, and the underlying pattern that eliminates the bug entirely.

Short answer: Remove the setState call from render. If you're syncing state to props, don't store derived state — compute it inline or with useMemo. If you're calling setState in response to a prop change, use useEffect (still not ideal) or the key prop to remount. If you're calling setState in a child during a parent's render, move the call to an event handler or a callback. The fix always removes the setState from the render phase.

✅ Solution 1 — Don't store derived state

The most common cause. Compute, don't store.

If a value can be computed from props or other state, it doesn't belong in useState. Storing it forces you to keep it in sync — usually via useEffect or setState during render. Both are unnecessary. Compute the value directly in the render function; the compiler optimizes the rest.

derived-state.tsx
import { useState } from 'react';

// ─── BAD: derived state in useState ──────────
function BadFullName() {
const [first, setFirst] = useState('Alice');
const [last, setLast] = useState('Smith');
const [full, setFull] = useState(first + ' ' + last); // ⚠️ derived

// ❌ Calling setState during render
setFull(first + ' ' + last);
// ⚠️ Warning: Cannot update a component while rendering

return 

{full}

; } // ─── GOOD: compute during render ──────────── function GoodFullName() { const [first, setFirst] = useState('Alice'); const [last, setLast] = useState('Smith'); // ✅ Computed every render — always correct, no state const full = first + ' ' + last; return (
setFirst(e.target.value)} /> setLast(e.target.value)} />

{full}

); } // ─── GOOD: useMemo for expensive derivations ─ function ExpensiveDerived({ items }: { items: number[] }) { // Only recompute when items changes const total = useMemo( () => items.reduce((sum, n) => sum + n, 0), [items] ); return

Total: {total}

; } // ─── Rule ──────────────────────────────────── // // If X can be computed from props or other state: // const X = compute(props, otherState); // // If that computation is expensive: // const X = useMemo(() => compute(props), [props]); // // Never: // const [X, setX] = useState(...); // setX(...) // in render — warning // useEffect(() => setX(...), [props]); // extra render, still wrong console.log(BadFullName, GoodFullName, ExpensiveDerived);
import { useState } from 'react';

function BadFullName() {
const [first, setFirst] = useState('Alice');
const [last, setLast] = useState('Smith');
const [full, setFull] = useState(first + ' ' + last);
setFull(first + ' ' + last);
return React.createElement('p', null, full);
}

function GoodFullName() {
const [first, setFirst] = useState('Alice');
const [last, setLast] = useState('Smith');
const full = first + ' ' + last;
return React.createElement('div', null,
React.createElement('input', { value: first, onChange: e => setFirst(e.target.value) }),
React.createElement('input', { value: last, onChange: e => setLast(e.target.value) }),
React.createElement('p', null, full)
);
}

function ExpensiveDerived({ items }) {
const total = useMemo(() => items.reduce((sum, n) => sum + n, 0), [items]);
return React.createElement('p', null, 'Total: ', total);
}

console.log(BadFullName, GoodFullName, ExpensiveDerived);

Compute during render

If a value derives from other state, compute it in the render function. No state, no sync, no warning.

useMemo for expensive

If the computation is slow, wrap in useMemo with correct dependencies. Only recomputes when deps change.

No useEffect for derivation

Even useEffect(() => setX(...), [props]) is wrong — it triggers an extra render. Compute instead.

The rule

State is for data that changes over time due to user interaction or external events. Everything else is computed.

The most common React mistake is storing too much in state. Every useState is a source of truth that can drift out of sync. If a value can be derived from other values, it should be. The bug pattern: you have props and state that disagree, and you add more state or effects to fix the disagreement. The right answer is usually to remove the derived state entirely. If you find yourself writing useEffect(() => setX(...), [something]), stop — you almost never need it. Compute the value in the render function.

✅ Solution 2 — Move setState to an event handler or effect

When the state genuinely responds to something.

Sometimes you need to update state in response to a user action or external event. That's what setState is for — but only inside event handlers, useEffect, or callbacks. Never inside render. If your code was calling setState during render to respond to a prop change, the correct fix is to move it to an event handler or use a useEffect.

event-handler.tsx
import { useState, useEffect } from 'react';

interface User {
id: string;
name: string;
email: string;
}

// ─── BAD: setting state during render ────────
function BadUserDisplay({ userId }: { userId: string }) {
const [user, setUser] = useStatenull>(null);

// ❌ Called during render
if (userId !== user?.id) {
setUser(fetchUserSync(userId));
}

return 

{user?.name ?? 'Loading...'}

; } // ─── GOOD: use useEffect for external data ─── function GoodUserDisplay({ userId }: { userId: string }) { const [user, setUser] = useStatenull>(null); useEffect(() => { let cancelled = false; fetch(/api/users/${userId}`) .then(r => r.json()) .then(data => { if (!cancelled) setUser(data); }); return () => { cancelled = true; }; }, [userId]); return

{user?.name ?? 'Loading...'}

; } // ─── GOOD: set state in an event handler ───── function Counter() { const [count, setCount] = useState(0); // ✅ User clicks the button → setCount runs in the event function handleClick() { setCount(c => c + 1); } return ( ); } // ─── GOOD: use a key to remount instead of syncing // If a component should reset its internal state when props // change, use key — it forces a fresh mount. function UserForm({ userId }: { userId: string }) { const [draft, setDraft] = useState(''); return (
setDraft(e.target.value)} />
); } function App({ userId }: { userId: string }) { // When userId changes, UserForm remounts with fresh state return ; } function fetchUserSync(id: string): User { return { id, name: 'Alice', email: 'a@x.com' }; } console.log(BadUserDisplay, GoodUserDisplay, Counter, UserForm, App);
import { useState, useEffect } from 'react';

function BadUserDisplay({ userId }) {
const [user, setUser] = useState(null);
if (userId !== user?.id) {
setUser(fetchUserSync(userId));
}
return React.createElement('p', null, user?.name ?? 'Loading...');
}

function GoodUserDisplay({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
let cancelled = false;
fetch(/api/users/${userId}`)
.then(r => r.json())
.then(data => { if (!cancelled) setUser(data); });
return () => { cancelled = true; };
}, [userId]);
return React.createElement('p', null, user?.name ?? 'Loading...');
}

function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(c => c + 1);
}
return React.createElement('button', { onClick: handleClick }, 'Count: ', count);
}

function UserForm({ userId }) {
const [draft, setDraft] = useState('');
return React.createElement('div', null,
React.createElement('input', { value: draft, onChange: e => setDraft(e.target.value) })
);
}

function App({ userId }) {
return React.createElement(UserForm, { key: userId, userId });
}

function fetchUserSync(id) {
return { id, name: 'Alice', email: 'a@x.com' };
}

console.log(BadUserDisplay, GoodUserDisplay, Counter, UserForm, App);

Event handler

onClick, onChange, onSubmit — these call setState. That's fine. Users trigger, state updates.

useEffect for external

Fetching, subscriptions, timers. Set state inside the effect, not during render. Cleanup cancels pending updates.

key for resets

If a component should reset when props change, pass a key. React remounts with fresh state — no sync needed.

Never in render

The render function is pure. No setState, no side effects, no DOM manipulation. Just compute the JSX.

✅ Solution 3 — Move setState out of the child's render

When a child component triggers a parent update during its own render.

Another variant: a child component calls setState on the parent — or on a shared store — during its own render. React warns because the parent hasn't finished rendering yet. The fix: move the update to a callback that runs after render (event handler, effect) — or lift the state higher so the update is local.

child-during-render.tsx
import { useState } from 'react';

// ─── BAD: child calls parent setState during render
function BadChild({ onMount }: { onMount: () => void }) {
// ❌ Called during render, before BadChild finishes
onMount();
return 

Child

; } function BadParent() { const [mounted, setMounted] = useState(false); return (
setMounted(true)} /> {/* ⚠️ Cannot update a component while rendering */}

{mounted ? 'mounted' : 'mounting'}

); } // ─── GOOD: use useEffect in the child ──────── function GoodChild({ onMount }: { onMount: () => void }) { useEffect(() => { // ✅ Runs after the child has mounted and rendered onMount(); }, []); return

Child

; } function GoodParent() { const [mounted, setMounted] = useState(false); return (
setMounted(true)} />

{mounted ? 'mounted' : 'mounting'}

); } // ─── ALTERNATIVE: lift state up ────────────── // If the parent needs to know something about the child, // the parent usually owns the state. Pass it down as props. function LiftedChild({ isActive }: { isActive: boolean }) { return

{isActive ? 'Active' : 'Inactive'}

; } function LiftedParent() { const [isActive, setIsActive] = useState(false); return (
); } // ─── The rules ─────────────────────────────── // // 1. Child never calls parent's setState during render. // 2. If a child needs to communicate something, // it's usually a callback called from an event, // effect, or a lifted state that lives in the parent. // 3. If you want the parent to "know" something, // the parent probably owns that state. import { useEffect } from 'react'; console.log(BadParent, GoodParent, LiftedParent);
import { useState, useEffect } from 'react';

function BadChild({ onMount }) {
onMount();
return React.createElement('p', null, 'Child');
}

function BadParent() {
const [mounted, setMounted] = useState(false);
return React.createElement('div', null,
React.createElement(BadChild, { onMount: () => setMounted(true) }),
React.createElement('p', null, mounted ? 'mounted' : 'mounting')
);
}

function GoodChild({ onMount }) {
useEffect(() => {
onMount();
}, []);
return React.createElement('p', null, 'Child');
}

function GoodParent() {
const [mounted, setMounted] = useState(false);
return React.createElement('div', null,
React.createElement(GoodChild, { onMount: () => setMounted(true) }),
React.createElement('p', null, mounted ? 'mounted' : 'mounting')
);
}

function LiftedChild({ isActive }) {
return React.createElement('p', null, isActive ? 'Active' : 'Inactive');
}

function LiftedParent() {
const [isActive, setIsActive] = useState(false);
return React.createElement('div', null,
React.createElement(LiftedChild, { isActive }),
React.createElement('button', { onClick: () => setIsActive(true) }, 'Activate')
);
}

console.log(BadParent, GoodParent, LiftedParent);

Child → parent updates

If a child needs to inform the parent, use a callback called from an event or effect. Never call it during render.

useEffect for lifecycle

onMount runs after render via useEffect(..., []). The parent's state updates after render finishes.

Lift state up

If the parent truly owns the state, don't ask the child to set it. The parent sets it in the event that triggered the change.

Data flows down

The React mental model: props flow down, callbacks flow up. A child doesn't "set" parent state — it calls a callback the parent provided.

This warning is a real bug, not a style nit. React 18+ makes calling setState during render an error because it can produce inconsistent UI — the parent might render before the child's update lands, or the update might be discarded in concurrent mode. Fix the actual data flow. Don't wrap it in setTimeout or queueMicrotask to silence the warning — that just moves the bug to a race condition that's harder to debug.

⚠️ Common mistakes

The patterns that trigger this warning.

1. Calling setState in the component body

function Component() { setX(...); return ...; } — warning. Fix: move to event handler or useEffect.

2. Syncing props to state with setState

if (props.x !== state) setState(props.x) during render. Fix: don't store props in state. Compute or use key.

3. Using useEffect to "fix" it

useEffect(() => setX(props.x), [props.x]) — no warning, but extra render + potential stale state. Fix: derive in render.

4. Calling setState in a callback during render

{items.map(i => { if (i.bad) setX(...); return ...; })} — warning. Fix: filter in render, or dispatch in an effect.

5. Using setState in a memo selector

React 18 concurrent mode is strict about this. Fix: derive in the component, not in selectors that run during render.

6. Child triggering parent update

function Child({ onMount }) { onMount(); return ...; } — warning. Fix: useEffect(() => onMount(), []).

7. Silencing with setTimeout

setTimeout(() => setX(...), 0) inside render — moves the bug. Fix: restructure the state.

8. Not noticing this in React 17

The warning existed in React 16-17 but was easy to ignore. React 18+ is stricter. Fix: it was always a bug.

❓ Frequently asked

What does "Cannot update a component while rendering" mean in React?

It means you called setState during a component's render phase — inside the component function, not inside an event handler or effect. React renders are supposed to be pure: same props, same JSX, no side effects. Calling setState during render schedules an update to another component (or the same one), which triggers a re-render during a render.

Why did this error start appearing after I upgraded React?

React 18+ is stricter about calling setState during render. The warning existed in React 16-17 but was easier to ignore. React 18 added concurrency features that make the warning a hard error in some cases. The fix is the same: don't call setState during render.

How do I fix it if I need to derive state from props?

Don't store derived state in useState. Compute it inline: instead of const [full, setFull] = useState(first + ' ' + last), write const full = first + ' ' + last. If computation is expensive, wrap in useMemo. If you must sync, use the key prop to remount the component when the input changes.

What is the difference between calling setState during render vs inside useEffect?

setState during render happens synchronously as React renders — it interrupts the render with a new render, which is what causes the warning. setState inside useEffect runs after the render completes, so React schedules another render normally. Effect-based setState is also not ideal but it's not a warning.

🔗 Related

← All React problems
Problem solved · React