This React warning occurs when a component calls useState() or useReducer() but the state update is not processed by React, often because it's called during render, in a callback that doesn't trigger a re-render, or in a way that violates React's update batching rules. Understanding when and where state updates should occur prevents this warning and ensures proper component behavior.
React tracks state updates and schedules re-renders when state changes. This warning appears when React detects that a component called useState() or useReducer() to update state, but that update was never processed through React's normal update cycle. This can happen when state updates are called in places where React cannot properly schedule a re-render, such as during the render phase itself, in asynchronous callbacks that don't trigger React's update mechanism, or when updates are batched incorrectly. The warning indicates a potential bug where state changes might not be reflected in the UI, leading to stale or inconsistent component state.
Ensure state updates occur in appropriate places: useEffect hooks, event handlers, or other React-managed callbacks. Never call setState directly in the component body.
// WRONG - state update during render
function MyComponent() {
const [count, setCount] = useState(0);
// This runs during every render!
setCount(count + 1);
return <div>{count}</div>;
}
// CORRECT - state update in useEffect
function MyComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
// This runs after render
setCount(prev => prev + 1);
}, []);
return <div>{count}</div>;
}When updating state in asynchronous code (setTimeout, promises, event listeners), ensure React can batch the updates properly. In React 18+, automatic batching works in most cases. For older versions or edge cases, use unstable_batchedUpdates or ensure updates happen in React-managed contexts.
import { unstable_batchedUpdates } from "react-dom"; // For React 17 and below
// In async callback without React 18 automatic batching
setTimeout(() => {
// React 17 and below need explicit batching
unstable_batchedUpdates(() => {
setCount(prev => prev + 1);
setLoading(false);
});
// React 18+ automatically batches most async updates
}, 1000);
// Alternative: Use React 18 createRoot with automatic batching
const root = createRoot(document.getElementById("root"));
root.render(<App />);State updates that occur after a component unmounts cause this warning. Clean up async operations in useEffect cleanup functions to prevent updates on unmounted components.
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
let isMounted = true;
fetchData().then(result => {
if (isMounted) {
setData(result); // Only update if component still mounted
}
});
return () => {
isMounted = false; // Cleanup: prevent updates after unmount
};
}, []);
return <div>{data}</div>;
}If using native DOM event listeners (addEventListener), state updates might not be batched properly. Use React synthetic event handlers instead, or wrap updates in unstable_batchedUpdates.
// WRONG - native event listener
useEffect(() => {
const handleClick = () => {
setCount(prev => prev + 1); // Might not batch properly
};
document.addEventListener("click", handleClick);
return () => document.removeEventListener("click", handleClick);
}, []);
// CORRECT - React synthetic event
return (
<button onClick={() => setCount(prev => prev + 1)}>
Click me
</button>
);
// CORRECT - native listener with batching
useEffect(() => {
const handleClick = () => {
unstable_batchedUpdates(() => {
setCount(prev => prev + 1);
});
};
document.addEventListener("click", handleClick);
return () => document.removeEventListener("click", handleClick);
}, []);If using React.memo with custom comparison or shouldComponentUpdate in class components, ensure they don't prevent re-renders when state actually changes. These optimizations can block state updates from being processed.
// WRONG - memo prevents re-renders even when state changes
const MyComponent = React.memo(function MyComponent({ count }) {
return <div>{count}</div>;
}, (prevProps, nextProps) => {
// Custom comparison that always returns true (no re-render)
return true;
});
// CORRECT - proper comparison
const MyComponent = React.memo(function MyComponent({ count }) {
return <div>{count}</div>;
}, (prevProps, nextProps) => {
// Only prevent re-render if count hasn't changed
return prevProps.count === nextProps.count;
});
// Class component with shouldComponentUpdate
class MyComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
// Allow update if state changed
return this.state.count !== nextState.count ||
this.props.data !== nextProps.data;
}
// ...
}When state updates depend on previous state, use the functional form of setState to ensure updates are applied correctly, especially in rapid succession or async contexts.
// WRONG - might use stale state in async context
const handleIncrement = () => {
setTimeout(() => {
setCount(count + 1); // Uses stale closure
}, 1000);
};
// CORRECT - functional update ensures fresh state
const handleIncrement = () => {
setTimeout(() => {
setCount(prev => prev + 1); // Always uses latest state
}, 1000);
};
// Also correct for multiple rapid updates
const handleMultiple = () => {
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
// Result: count increases by 3
};Enable React.StrictMode during development to identify unsafe lifecycle methods and problematic update patterns. StrictMode double-invokes render methods and effects, helping catch issues where updates might be missed.
// In your main app file
import React from "react";
import ReactDOM from "react-dom/client";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// Check console for additional warnings about unsafe lifecycle usage
// StrictMode helps identify:
// - State updates during render
// - Effects with missing dependencies
// - Legacy lifecycle method usageThis warning is more common in React 18+ due to concurrent features and stricter update tracking. In concurrent mode, React can interrupt, pause, or discard renders, which can lead to updates not being processed if they're not properly scheduled. The warning often appears alongside "Cannot update a component while rendering a different component" if there are render-phase state updates. For complex state management, consider using state management libraries like Redux or Zustand that handle updates outside React components. When using React.lazy or Suspense, ensure state updates occur after components are properly loaded. In server components (React 18+), state updates are not allowed at all, so this warning would indicate client component boundaries being crossed.
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite