The Error
Open the browser console and you'll see something like this โ usually repeated several times:
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
The app might still run. But React is flagging something genuinely dangerous: a child component is triggering a state update in a parent during the render phase. React 16.13 introduced this warning. In React 18 concurrent mode, it can escalate into an actual crash.
Root Cause
React's render phase must be a pure computation โ no side effects, no state updates, nothing that changes the world outside the function. When Child calls a prop callback that sets state on App directly inside its render body โ not inside an event handler or effect โ it schedules a state update while React is still mid-render. That's a contract violation.
Three patterns trigger this most often:
- Calling a parent's setter function unconditionally at the top of a component
- Calling
setStateinside a render-phase callback passed as a prop - A ref callback or render prop that fires state updates synchronously
Reproduce the Bug
Here's the minimal case that triggers the warning:
// โ Broken โ Child calls onMount during render, not in an effect
function Child({ onMount }) {
onMount('Child is here'); // runs during render!
return <div>Child</div>;
}
function App() {
const [message, setMessage] = React.useState('');
return (
<div>
<p>{message}</p>
<Child onMount={setMessage} />
</div>
);
}
Every time App renders, it renders Child. Child immediately calls setMessage โ a state setter on App. That queues another render of App. React catches this mid-flight and logs the warning. In a worst case, this creates an infinite render loop.
Step-by-Step Fix
Fix 1: Move the call into useEffect (most common fix)
To notify a parent when a child mounts or a value changes, put the call inside useEffect:
// โ
Fixed โ side effect happens after render, not during
function Child({ onMount }) {
React.useEffect(() => {
onMount('Child is here');
}, []); // empty deps = run once after mount
return <div>Child</div>;
}
function App() {
const [message, setMessage] = React.useState('');
return (
<div>
<p>{message}</p>
<Child onMount={setMessage} />
</div>
);
}
useEffect runs after the browser paints. The parent state update lands in the next cycle โ no render conflict, no warning.
Fix 2: Derive state instead of syncing it
Sometimes this warning is a code smell. It reveals you're manually syncing state that could simply be derived from props or existing state. Ask yourself: does App already have the data Child is trying to report back?
// โ Anti-pattern: child mirrors data back to parent via callback during render
function Child({ value, onValueSeen }) {
onValueSeen(value); // triggers warning
return <span>{value}</span>;
}
// โ
Better: parent owns the data, child just displays it
function App() {
const [value] = React.useState('hello');
// No need for Child to report back โ App already has `value`
return <Child value={value} />;
}
Fix 3: Move state down into the child
State that only one component needs shouldn't live in a parent. Push it down:
// โ Parent holds state that only child uses
function App() {
const [isOpen, setIsOpen] = React.useState(false);
return <Modal isOpen={isOpen} onToggle={setIsOpen} />;
}
// โ
Child manages its own open/close state
function Modal() {
const [isOpen, setIsOpen] = React.useState(false);
return (
<div>
<button onClick={() => setIsOpen(o => !o)}>Toggle</button>
{isOpen && <div>Modal content</div>}
</div>
);
}
Fix 4: Check third-party component callbacks
Libraries can be the culprit too. Data grids, form libraries, and virtualized lists sometimes call your callback during their own render cycle. A common offender:
// โ onRowsChange fires during DataGrid's render cycle
<DataGrid
rows={rows}
onRowsChange={(newRows) => setRows(newRows)} // may fire during render
/>
// โ
Wrap in useCallback and check the library docs
const handleRowsChange = React.useCallback((newRows) => {
setRows(newRows);
}, []);
<DataGrid rows={rows} onRowsChange={handleRowsChange} />
A library that consistently fires during render has a bug on its end. Check for a newer version, or defer the state update with useEffect.
Debugging Strategy
The warning names the exact components involved. Use that to your advantage:
- Open DevTools โ Console
- Click the stack trace link next to the warning
- Find the frame inside your
Childcomponent (skip React internals) - That line is where
setStatefires โ check whether it's inside an event handler,useEffect, or the raw render body
Stack trace minified? Run the dev build (npm start or next dev). React only emits this warning in development mode โ it won't appear in production builds.
Verify the Fix
After applying your fix, run through this checklist:
- Hard-refresh the page (
Ctrl+Shift+R/Cmd+Shift+R) - Open the console โ the warning should be gone
- Walk through the affected flow: mount, update, and unmount the child component
- In React DevTools Profiler, confirm render counts are stable with no cascading re-renders
One React 18 gotcha worth knowing: Strict Mode intentionally runs effects twice on mount in development. If your useEffect fix causes the parent setter to fire twice, add a cleanup flag:
React.useEffect(() => {
let active = true;
if (active) onMount('Child is here');
return () => { active = false; };
}, []);
Lessons Learned
- Render must be pure. Never call setState โ directly or through a callback prop โ at the top level of a component. That's what event handlers and
useEffectare for. - A prop callback isn't an event handler. Passing
setFooas a prop doesn't make calling it safe during render. The receiving component controls when it fires. - Treat this as an error, not a warning. React 18 concurrent mode makes render-phase side effects non-deterministic. A harmless warning today can silently corrupt state once concurrent features kick in.
- Default to useEffect for mount-time notifications. Any time a child needs to inform a parent of something at mount,
useEffectwith an empty dependency array is the right tool.

