![]()
A dangerous React anti-pattern is calling state updaters (like setState or state setters from hooks) directly inside the render() method or function component body, which creates infinite re-render loops.
The Problem
// ❌ Class component infinite loop
class BadComponent extends React.Component {
  render() {
    this.setState({ count: 1 }); // Triggers re-render
    return <div>Count: {this.state.count}</div>;
  }
}
// ❌ Functional component infinite loop
function BadComponent() {
  const [count, setCount] = useState(0);
  setCount(1); // Triggers re-render
  return <div>Count: {count}</div>;
}
Why this happens:
render()runs → calls state updater- State update triggers re-render
 - New render calls state updater again
 - Infinite loop continues
 
Correct Solutions
1. Initialize State Properly
// ✅ Class component - set initial state in constructor
class GoodComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 1 }; // Initial state
  }
  render() {
    return <div>Count: {this.state.count}</div>;
  }
}
// ✅ Functional component - pass initial state to useState
function GoodComponent() {
  const [count] = useState(1); // Initial state
  return <div>Count: {count}</div>;
}
2. Update State in Event Handlers or Effects
// ✅ Class component - update in handler
class Counter extends React.Component {
  state = { count: 0 };
  increment = () => {
    this.setState({ count: this.state.count + 1 });
  };
  render() {
    return (
      <button onClick={this.increment}>
        Count: {this.state.count}
      </button>
    );
  }
}
// ✅ Functional component - update in useEffect or handler
function Counter() {
  const [count, setCount] = useState(0);
  // Or in useEffect if needed for side effects
  useEffect(() => {
    // Safe to set state here if properly conditioned
  }, [dependencies]);
  return (
    <button onClick={() => setCount(c => c + 1)}>
      Count: {count}
    </button>
  );
}
When State Updates Are Safe in Render
There’s one exception where state updates during render are acceptable – when using the “derived state” pattern with getDerivedStateFromProps (class components) or calling setters during rendering in a way that doesn’t cause infinite loops:
// ✅ Acceptable - derived state pattern
class DerivedState extends React.Component {
  state = { derivedValue: 0 };
  static getDerivedStateFromProps(props, state) {
    // Calculate new state based on props
    return { derivedValue: props.someValue };
  }
  render() {
    return <div>{this.state.derivedValue}</div>;
  }
}
// ✅ Functional component equivalent
function DerivedState({ someValue }) {
  const [derivedValue, setDerivedValue] = useState(0);
  // Only update if prop changed
  if (someValue !== derivedValue) {
    setDerivedValue(someValue);
  }
  return <div>{derivedValue}</div>;
}
Common Infinite Loop Scenarios
- Data fetching in render:
 
   function UserProfile() {
     const [user, setUser] = useState(null);
     fetch('/user').then(r => r.json()).then(setUser); // ❌ Bad
     return <div>{user?.name}</div>;
   }
- Recursive state updates:
 
   function Counter() {
     const [count, setCount] = useState(0);
     setCount(count + 1); // ❌ Bad - infinite
     return <div>{count}</div>;
   }
- State based on props:
 
   function Display({ value }) {
     const [internal, setInternal] = useState(value);
     setInternal(value); // ❌ Bad - runs every render
     return <div>{internal}</div>;
   }
Best Practices
- Never call state updaters unconditionally in render
 - Move side effects to 
useEffect(functional) or lifecycle methods (class) - Use event handlers for user interaction-driven updates
 - For derived state, compare props/state before updating
 - Consider useMemo/useCallback for expensive calculations
 
