![]()
Avoiding Infinite Loops When Setting State in React
The most common cause of infinite loops in React occurs when you call setState (or a state updater function from hooks) directly inside the render() method (or the function component body).
Why This Causes an Infinite Loop
- Render → State Update → Re-render Cycle:
render()runs- You call
setState() - State update triggers a re-render
render()runs again- You call
setState()again - …and the cycle continues indefinitely
Class Component Example (Wrong)
class InfiniteLoopExample extends React.Component {
state = { count: 0 };
render() {
// ❌ WRONG - Causes infinite loop
this.setState({ count: this.state.count + 1 });
return <div>Count: {this.state.count}</div>;
}
}
Function Component Example (Wrong)
function InfiniteLoopExample() {
const [count, setCount] = useState(0);
// ❌ WRONG - Causes infinite loop
setCount(count + 1);
return <div>Count: {count}</div>;
}
How to Fix It
1. Move State Updates to Event Handlers or Lifecycle Methods
Class Component Solution:
class SafeExample extends React.Component {
state = { count: 0 };
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<button onClick={this.increment}>Increment</button>
<div>Count: {this.state.count}</div>
</div>
);
}
}
Function Component Solution:
function SafeExample() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
return (
<div>
<button onClick={increment}>Increment</button>
<div>Count: {count}</div>
</div>
);
}
2. Use useEffect for Side Effects (Function Components)
If you need to set state based on props or other state:
function SafeEffectExample({ initialValue }) {
const [value, setValue] = useState(initialValue);
// ✅ Correct - Only runs when initialValue changes
useEffect(() => {
setValue(initialValue);
}, [initialValue]);
return <div>Value: {value}</div>;
}
3. Use componentDidUpdate (Class Components)
class SafeUpdateExample extends React.Component {
state = { derivedValue: this.props.inputValue };
componentDidUpdate(prevProps) {
if (this.props.inputValue !== prevProps.inputValue) {
// ✅ Correct - Only updates when prop changes
this.setState({ derivedValue: this.props.inputValue });
}
}
render() {
return <div>Derived: {this.state.derivedValue}</div>;
}
}
Key Takeaways
- Never call state setters directly in render – This will always cause an infinite loop
- Use event handlers for user-initiated state changes
- Use lifecycle methods/effects for derived state or side effects
- Always include proper dependencies in
useEffectto prevent unnecessary updates
Remember: The render method should be a pure function of props and state – it shouldn’t cause side effects like state updates.
