This error occurs when a React component attempts to call setState or dispatch a state update while the component is currently rendering. This disrupts React's rendering cycle and can cause infinite loops or unpredictable behavior.
This error is React's way of preventing a common anti-pattern where state updates happen during the render phase. When a component is rendering, React is calculating what the UI should look like based on the current state and props. If you try to update state during this calculation, it creates a circular dependency: the render triggers a state update, which triggers another render, which triggers another state update, and so on. React's rendering process must be a pure function - given the same inputs (state and props), it should always produce the same output (JSX). State updates during render violate this principle because they introduce side effects into the render phase. This can lead to infinite loops, inconsistent UI states, performance degradation, and unpredictable component behavior. The error typically manifests as a warning in development mode before causing more serious issues. React 18 and later versions have become more strict about detecting and warning about these patterns to help developers catch them early.
If you need to update state in response to prop changes or side effects, use the useEffect hook:
// ❌ Bad: setState during render
function MyComponent({ userId }) {
const [user, setUser] = useState(null);
// This runs during render and causes the error
if (userId !== user?.id) {
setUser(null); // Don't do this!
}
return <div>{user?.name}</div>;
}
// ✅ Good: setState in useEffect
function MyComponent({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
// State update happens after render
setUser(null);
// Fetch new user data...
}, [userId]); // Only run when userId changes
return <div>{user?.name}</div>;
}For functional components, always perform state updates inside useEffect with proper dependencies specified.
Ensure your useEffect hooks have complete and correct dependency arrays to prevent updates on every render:
// ❌ Bad: Missing dependency array causes infinite loop
function MyComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(count + 1); // Runs on every render!
}); // No dependency array
return <div>{count}</div>;
}
// ✅ Good: Empty array runs once, or specify dependencies
function MyComponent() {
const [count, setCount] = useState(0);
useEffect(() => {
// This runs once on mount
const timer = setTimeout(() => setCount(1), 1000);
return () => clearTimeout(timer);
}, []); // Empty array = run once
return <div>{count}</div>;
}An empty dependency array means the effect runs once after mount. Include dependencies that the effect actually depends on.
State updates should happen in response to user events, not during render:
// ❌ Bad: Calling updater during render
function SearchBox({ onSearch }) {
const [query, setQuery] = useState('');
// This runs during every render!
setQuery(getDefaultQuery());
return <input value={query} />;
}
// ✅ Good: Initialize with proper default or update in handler
function SearchBox({ onSearch }) {
const [query, setQuery] = useState(() => getDefaultQuery());
const handleChange = (e) => {
setQuery(e.target.value);
onSearch(e.target.value);
};
return <input value={query} onChange={handleChange} />;
}Use lazy initialization for expensive defaults: useState(() => computeDefault()).
When a child component needs to notify a parent, use callbacks instead of direct state updates:
// ❌ Bad: Child updating parent during render
function Parent() {
const [data, setData] = useState(null);
return <Child onDataChange={setData} />;
}
function Child({ onDataChange }) {
const result = calculateSomething();
onDataChange(result); // Called during render!
return <div>{result}</div>;
}
// ✅ Good: Child uses effect or event handler
function Parent() {
const [data, setData] = useState(null);
return <Child onDataChange={setData} />;
}
function Child({ onDataChange }) {
const result = calculateSomething();
useEffect(() => {
onDataChange(result); // Called after render
}, [result, onDataChange]);
return <div>{result}</div>;
}Alternatively, lift the state calculation to the parent component to avoid the synchronization entirely.
Often you don't need to update state at all - you can derive values from existing state:
// ❌ Bad: Updating state based on other state
function TodoList({ todos }) {
const [filteredTodos, setFilteredTodos] = useState([]);
const [filter, setFilter] = useState('all');
// Anti-pattern: updating state during render
if (filter === 'active') {
setFilteredTodos(todos.filter(t => !t.completed));
}
return <div>{filteredTodos.map(...)}</div>;
}
// ✅ Good: Derive state from props/state
function TodoList({ todos }) {
const [filter, setFilter] = useState('all');
// Calculated during render (no state update needed)
const filteredTodos = useMemo(() => {
return todos.filter(t =>
filter === 'all' ||
(filter === 'active' && !t.completed) ||
(filter === 'completed' && t.completed)
);
}, [todos, filter]);
return <div>{filteredTodos.map(...)}</div>;
}If a value can be computed from existing state or props, don't store it in state - compute it during render.
React 18+ Automatic Batching: React 18 introduced automatic batching for all state updates, including those in promises, setTimeout, and native event handlers. This reduces unnecessary re-renders but doesn't eliminate the render-phase update restriction.
useEffectEvent (Experimental): React 19 introduces useEffectEvent to solve a related problem where synchronous setState calls within effects can trigger cascading renders. This hook allows you to access the latest props/state without including them in the dependency array.
Class Components: In class components, never call setState in the render() method. Use componentDidMount, componentDidUpdate, or event handlers instead. The getDerivedStateFromProps static method is designed for the specific use case of updating state based on prop changes.
StrictMode Detection: React's StrictMode in development intentionally double-invokes render functions to help detect impure rendering logic. If you see this error only in development, check if you have state updates in your render logic.
Performance Impact: Beyond causing errors, state updates during render severely impact performance. Each update triggers a new render cycle, and if done repeatedly, it can freeze the entire application. React's concurrent features in React 18+ make this even more critical as they rely on predictable rendering behavior.
Debugging Strategy: Use React DevTools Profiler to identify which component is causing excessive renders. Look for components with very high render counts or those that render every time their parent renders. Add console.logs with component names to trace the update chain.
Prop spreading could cause security issues
Prop spreading could cause security issues
Error: error:0308010C:digital envelope routines::unsupported
Error: error:0308010C:digital envelope routines::unsupported
React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render.
React Hook useEffect placed inside a condition
Hook can only be called inside the body of a function component
Hook can only be called inside the body of a function component
Rollup failed to resolve import during build
How to fix "Rollup failed to resolve import" in React