Building Your Learning Module...
Getting things ready for you!
Find videos you like?
Save to resource drawer for future reference!
In React, a side effect is any operation that affects something outside the component's render cycle. React components are meant to be pure - they take props and state and return UI. Side effects break this purity by interacting with the external world.
API calls, database queries, network requests
setTimeout, setInterval, countdown logic
Direct DOM access, focus management, measurements
WebSockets, event listeners, observables
localStorage, geolocation, notifications
Tracking, logging, metrics collection
This causes infinite loops and performance issues
function BrokenCounter() {
const [count, setCount] = React.useState(0);
// ❌ WRONG: Side effect during render!
// This runs every time the component renders
document.title = `Count: ${count}`;
// This causes infinite loop:
// 1. Component renders
// 2. Side effect runs
// 3. setCount triggers re-render
// 4. Go back to step 1
if (count < 10) {
setCount(count + 1);
}
return (
<div className="container">
<h1>🚨 Broken Counter</h1>
<p>This component has serious issues!</p>
<div className="counter">{count}</div>
<div className="error">
<h3>⚠️ Problems:</h3>
<ul>
<li>Infinite re-render loop</li>
<li>Performance degradation</li>
<li>Unpredictable behavior</li>
<li>Memory leaks</li>
</ul>
</div>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<BrokenCounter />);Loading preview...
Clean separation of concerns and proper lifecycle
function CorrectCounter() {
const [count, setCount] = React.useState(0);
const [isRunning, setIsRunning] = React.useState(true);
// ✅ CORRECT: Side effect in useEffect
React.useEffect(() => {
// This runs AFTER render, not during
document.title = `Count: ${count}`;
}, [count]); // Only runs when count changes
// ✅ CORRECT: Timer in useEffect with cleanup
React.useEffect(() => {
if (!isRunning) return;
const interval = setInterval(() => {
setCount(c => c + 1);
}, 1000);
// Cleanup function
return () => clearInterval(interval);
}, [isRunning]);
return (
<div className="container">
<h1>✅ Correct Counter</h1>
<p>Check the browser tab title!</p>
<div className="counter">{count}</div>
<button
onClick={() => setIsRunning(!isRunning)}
className="btn-toggle"
>
{isRunning ? 'Pause' : 'Start'}
</button>
<div className="success">
<h3>✅ Benefits:</h3>
<ul>
<li>Controlled execution</li>
<li>Proper cleanup</li>
<li>Performance optimized</li>
<li>Predictable behavior</li>
</ul>
</div>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<CorrectCounter />);Loading preview...
Rendering and side effects have different responsibilities. useEffect enforces this separation by moving side effects out of the render cycle.
Effects run after React has updated the DOM. This prevents layout thrashing and ensures the UI is ready before side effects execute.
The cleanup function pattern ensures resources are released properly, preventing memory leaks and race conditions.
Instead of imperative "when this happens, do that," useEffect lets you declare "keep this in sync with that."
Anything affecting the world outside React's render cycle needs useEffect.
This ensures the UI is updated before side effects execute, preventing visual glitches.
The dependency array tells React when to re-run your effect based on data changes.
Always return a cleanup function for subscriptions, timers, and external resources.