Skip to content

Fix: React Cannot update a component while rendering a different component

FixDevs · (Updated: )

Part of:  React & Frontend Errors

Quick Answer

How to fix React Cannot update a component while rendering a different component caused by setState during render, context updates in render, and Redux dispatch in render.

The Error

Your React app logs this warning (React 18+) or throws an error:

Warning: Cannot update a component (`ParentComponent`) while rendering a different component (`ChildComponent`).
To locate the bad setState() call inside `ChildComponent`, follow the component stack trace.

Or variations:

Warning: Cannot update a component from inside the function body of a different component.
Warning: Cannot update during an existing state transition (such as within `render`).
Render methods should be a pure function of props and state.

You called setState, dispatched a Redux action, or updated a context value during the render phase of another component. React’s render phase must be pure — no side effects.

Why This Happens

React renders components by calling their function body (or render() method in class components). During this render phase, React expects the function to be pure: it should only compute the output based on props and state, with no side effects.

When you call setState (or any state update) during the render of a different component, React detects this violation and warns you. The reason React is strict about this is the reconciler. The render phase produces a description of the next UI tree; the commit phase applies it. If a component schedules an update to another component while the reconciler is still walking the current tree, React has to discard partial work and re-start. Concurrent rendering (React 18+) makes the situation worse — a render may be paused, resumed, or thrown away, and any side effect performed mid-render runs an unbounded number of times.

The error specifically triggers when the update target is a different component from the one currently rendering. A component is allowed to call setState on itself during render in narrow cases (the “derive state during render” pattern), because React can detect that and bail out cleanly. But updating a parent, sibling, or context provider from inside a child’s render escapes that detection and corrupts the tree’s render order. The fix is always to defer the side effect into a phase React designed for it: useEffect, an event handler, or a useEffect cleanup.

This happens because:

  • Setting state during render triggers another render, which can cause infinite loops.
  • React cannot guarantee consistent rendering if components update each other’s state during rendering.
  • The render phase should be predictable — the same props and state should always produce the same output.

Common causes:

  • Calling a callback prop that sets parent state during render.
  • Dispatching Redux actions during render.
  • Updating context values during render.
  • Calling setState directly in the function body (not inside useEffect or an event handler).
  • Using useSyncExternalStore or subscribing to external stores incorrectly.

In Production: Incident Lens

In production this warning rarely shows up as a console message because production builds of React (NODE_ENV=production) strip the dev-time warning. Instead it manifests as a hydration mismatch, an unexpectedly blank section of the UI, or a Sentry-level error from the React error boundary several components up. The classic incident is “fine on staging, broken on the real traffic shape” — the conditional setState only triggers when a specific feature flag, locale, or user role is loaded, and that combination never appeared in your QA accounts. The blast radius is per-component but cascades: the offending child throws or returns stale UI, which causes its parent’s render to bail out, which causes everything below that parent to disappear behind the nearest error boundary.

The monitoring signal is a sudden spike in your React error boundary handler, ideally piped to Sentry’s React integration, plus a correlated drop in interaction telemetry for the affected route. Wire the error boundary to send the component stack and the user’s flag context so you can reproduce. Synthetic checks against the production URL with realistic auth state catch this category — synthetics that always run as an admin user will miss it.

Recovery is almost always forward-fix, not rollback. The reason is data: by the time the error shows up in production, real users have already taken actions (signed up, paid, posted content) on the new deploy. Rolling back means either losing that data or running a complex backfill. Cut a hotfix that moves the offending setState into useEffect, ship it, and only roll back if the forward path will take longer than the SLO error budget allows. The postmortem preventive is two-layered: enable <React.StrictMode> in development and CI so the double-render exposes side effects locally, and add the react-hooks/exhaustive-deps and react-hooks/rules-of-hooks ESLint rules at error level in your pre-merge pipeline. Snapshot tests under StrictMode often catch the violation as a duplicate state update before it ever ships.

Fix 1: Move State Updates to useEffect

The most common fix. State updates based on props or other state should happen in useEffect:

Broken — setState during render:

function ChildComponent({ items, onCountChange }) {
  const count = items.length;
  onCountChange(count);  // Calls parent's setState during render!

  return <div>{count} items</div>;
}

function ParentComponent() {
  const [count, setCount] = useState(0);
  const items = useItems();

  return <ChildComponent items={items} onCountChange={setCount} />;
}

Fixed — use useEffect:

function ChildComponent({ items, onCountChange }) {
  const count = items.length;

  useEffect(() => {
    onCountChange(count);  // Runs after render, not during
  }, [count, onCountChange]);

  return <div>{count} items</div>;
}

Fixed — derive the value instead of syncing state:

function ParentComponent() {
  const items = useItems();
  const count = items.length;  // Derive directly — no state sync needed!

  return (
    <>
      <div>Total: {count}</div>
      <ItemList items={items} />
    </>
  );
}

Pro Tip: Before adding useEffect to sync state, ask: “Can I derive this value from existing state or props?” Derived values do not need their own state. This eliminates the entire category of “update during render” bugs and reduces unnecessary re-renders.

Fix 2: Fix Callbacks That Set State

A common pattern is passing a callback prop that gets called during render:

Broken — onLoad callback during render:

function DataLoader({ url, onLoad }) {
  const data = useFetchedData(url);

  if (data) {
    onLoad(data);  // Called during render!
  }

  return <div>{data ? "Loaded" : "Loading..."}</div>;
}

function App() {
  const [data, setData] = useState(null);
  return <DataLoader url="/api/data" onLoad={setData} />;
}

Fixed — use useEffect for the callback:

function DataLoader({ url, onLoad }) {
  const data = useFetchedData(url);

  useEffect(() => {
    if (data) {
      onLoad(data);
    }
  }, [data, onLoad]);

  return <div>{data ? "Loaded" : "Loading..."}</div>;
}

Fixed — lift state up instead of syncing:

function App() {
  const data = useFetchedData("/api/data");

  return (
    <div>
      {data ? <DataDisplay data={data} /> : <Loading />}
    </div>
  );
}

Fix 3: Fix Redux Dispatch During Render

Dispatching Redux actions during render causes the same issue:

Broken:

function UserProfile({ userId }) {
  const dispatch = useDispatch();
  const user = useSelector(state => state.users[userId]);

  if (!user) {
    dispatch(fetchUser(userId));  // Dispatch during render!
  }

  return user ? <div>{user.name}</div> : <div>Loading...</div>;
}

Fixed — dispatch in useEffect:

function UserProfile({ userId }) {
  const dispatch = useDispatch();
  const user = useSelector(state => state.users[userId]);

  useEffect(() => {
    if (!user) {
      dispatch(fetchUser(userId));
    }
  }, [userId, user, dispatch]);

  return user ? <div>{user.name}</div> : <div>Loading...</div>;
}

Fixed — with RTK Query (automatic fetching):

function UserProfile({ userId }) {
  const { data: user, isLoading } = useGetUserQuery(userId);

  if (isLoading) return <div>Loading...</div>;
  return <div>{user.name}</div>;
}

Fix 4: Fix Context Updates During Render

Updating context value during render:

Broken:

function ThemeDetector({ children }) {
  const { setTheme } = useContext(ThemeContext);
  const prefersDark = useMediaQuery('(prefers-color-scheme: dark)');

  setTheme(prefersDark ? 'dark' : 'light');  // Updates context during render!

  return children;
}

Fixed:

function ThemeDetector({ children }) {
  const { setTheme } = useContext(ThemeContext);
  const prefersDark = useMediaQuery('(prefers-color-scheme: dark)');

  useEffect(() => {
    setTheme(prefersDark ? 'dark' : 'light');
  }, [prefersDark, setTheme]);

  return children;
}

Fix 5: Fix Conditional State Initialization

Setting state based on a condition during render:

Broken:

function SearchResults({ query, results }) {
  const [selectedId, setSelectedId] = useState(null);

  // Resets selection when results change — BUT does it during render!
  if (results.length > 0 && selectedId === null) {
    setSelectedId(results[0].id);
  }

  return <ResultsList results={results} selectedId={selectedId} />;
}

Fixed — use useEffect:

function SearchResults({ query, results }) {
  const [selectedId, setSelectedId] = useState(null);

  useEffect(() => {
    if (results.length > 0) {
      setSelectedId(results[0].id);
    }
  }, [results]);

  return <ResultsList results={results} selectedId={selectedId} />;
}

Fixed — use a key to reset state (React pattern):

function App() {
  const [query, setQuery] = useState("");
  const results = useSearch(query);

  // key={query} unmounts/remounts SearchResults, resetting its state
  return <SearchResults key={query} results={results} />;
}

function SearchResults({ results }) {
  // State is fresh on every new query because the component remounts
  const [selectedId, setSelectedId] = useState(
    results.length > 0 ? results[0].id : null
  );

  return <ResultsList results={results} selectedId={selectedId} />;
}

Common Mistake: Using useEffect to “sync” state that could be derived or using it to reset state when props change. Consider using the key prop to reset component state, or derive the value directly from props without state.

Fix 6: Fix Custom Hooks That Set State

Custom hooks that set state during execution:

Broken:

function useFormValidation(values, setErrors) {
  const errors = validate(values);
  if (Object.keys(errors).length > 0) {
    setErrors(errors);  // Sets parent state during render!
  }
  return errors;
}

Fixed — return the errors, let the caller handle them:

function useFormValidation(values) {
  return useMemo(() => validate(values), [values]);
}

function MyForm() {
  const [values, setValues] = useState({});
  const errors = useFormValidation(values);
  // errors is derived, no state sync needed
}

Fix 7: Fix Class Component render() Side Effects

In class components, side effects in render() cause the same issue:

Broken:

class Dashboard extends React.Component {
  render() {
    if (this.props.data.length > this.state.lastCount) {
      this.props.onDataChange(this.props.data.length);  // Side effect in render!
    }

    return <div>{this.props.data.length} items</div>;
  }
}

Fixed — use componentDidUpdate:

class Dashboard extends React.Component {
  componentDidUpdate(prevProps) {
    if (this.props.data.length !== prevProps.data.length) {
      this.props.onDataChange(this.props.data.length);
    }
  }

  render() {
    return <div>{this.props.data.length} items</div>;
  }
}

Fix 8: Use useDeferredValue or startTransition

For state updates that should not block rendering:

import { useDeferredValue, startTransition } from 'react';

function SearchPage() {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);

  return (
    <>
      <input value={query} onChange={e => setQuery(e.target.value)} />
      <Results query={deferredQuery} />
    </>
  );
}

Using startTransition for non-urgent updates:

function handleClick() {
  startTransition(() => {
    setExpensiveState(computeNewState());
  });
}

Still Not Working?

Use React Strict Mode to catch these issues early. Strict Mode intentionally double-renders components to expose side effects:

<React.StrictMode>
  <App />
</React.StrictMode>

Check for third-party libraries that set state during render. Some older libraries are not compatible with React 18’s stricter rendering model.

Check for useSyncExternalStore issues. If you subscribe to an external store, ensure the getSnapshot function returns a consistent value during rendering.

Check for hydration mismatches caused by the same root cause. If the server-rendered HTML differs from the client’s first render — often because a setState ran during render only on the client — the error shows up as a hydration warning instead. See Fix: React hydration error for the diagnostic checklist.

Audit any code that runs during render. Pull every if (something) doSideEffect() out of the function body and into useEffect. A useful refactor is to forbid any function call that doesn’t return data from being made directly inside the component body — log writes, analytics events, store dispatches, ref assignments, and parent callbacks all belong inside useEffect or an event handler.

Check React DevTools’ “Highlight updates when components render.” Toggle it on, then reproduce the bug. Components that flash every frame are stuck in a render→setState→render loop, which is the most common downstream symptom of this error escaping unnoticed.

For infinite re-render loops, see Fix: React useEffect infinite loop. For rendering object errors, see Fix: Objects are not valid as a React child. For missing dependency warnings, see Fix: React useEffect missing dependency.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles