Module 5.2: useEffect Masterclass

🎯 Learning Objectives
After this lesson, you’ll master:
- Dependency Array: Gain a deep understanding of the differences between no dependencies, an empty dependency
array[], and anarraywith dependencies[a, b]. - Advanced Cleanup Function: Learn to handle unregistering global events (Window/Document Events).
- Data Fetching: Explore the basic
functionpattern for calling APIs insideuseEffectand updatingstate. - Race Conditions: Recognize the issues that arise from making continuous API calls.
1. Deep Dive: Dependency Array
The Dependency Array is useEffect’s second argument. It determines WHEN your effect re-runs.

1.1. Detailed Comparison
| Code | Meaning | Behavior |
|---|---|---|
useEffect(fn) | No Array | Runs after EVERY render. ⚠️ Dangerous: Easily causes an Infinite Loop if you set state inside. |
useEffect(fn, []) | Empty Array | Runs ONLY ONCE after mounting. ✅ Perfect for: Initial API calls, Event Listeners. |
useEffect(fn, [a, b]) | With Variables | Runs initially AND whenever a or b’s value changes.✅ Ideal for: Synchronizing logic (e.g., search keyword changes -> re-call API). |
1.2. Infinite Loop Trap
A classic pitfall: Setting a dependency on a state that the effect itself is modifying.
// ❌ INCORRECT: Creates an infinite loop
useEffect(() => {
setCount(count + 1); // 1. Changes count -> Triggers Re-render
}, [count]); // 2. Count changes -> Triggers Effect -> Back to 1How to Fix: Use a functional update or remove the dependency if it’s not needed.
// ✅ CORRECT
useEffect(() => {
const timer = setInterval(() => {
setCount((prev) => prev + 1); // Doesn't need to depend on the current 'count'
}, 1000);
return () => clearInterval(timer);
}, []); // Only runs once to set up the timer2. Cleanup Function: Global Events
When you subscribe to events on the window (e.g., resize, scroll, keydown), you MUST remove the listener when the component unmounts. Otherwise, the event will duplicate every time the component re-renders, leading to memory leaks and unexpected behavior.
Standard Procedure:
- Write the event handler
function. window.addEventListenerin theeffectbody (Mount).window.removeEventListenerin thereturnfunction(Unmount).
useEffect(() => {
// 1. Define handler
const handleResize = () => {
console.log(window.innerWidth);
};
// 2. Add listener (Mount)
window.addEventListener('resize', handleResize);
// 3. Cleanup (Unmount)
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);3. Data Fetching (Calling API)
In vanilla React (without libraries like React Query), we call APIs inside useEffect and save the results to state.

Standard Code Example
import { useState, useEffect } from 'react';
interface User {
id: number;
name: string;
}
function UserList() {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
// 1. Create an async function inside the effect
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch('https://jsonplaceholder.typicode.com/users');
if (!response.ok) throw new Error('Network error');
const data: User[] = await response.json();
setUsers(data); // Update data
} catch (err: any) {
setError(err.message); // Handle error
} finally {
setLoading(false); // Stop loading
}
};
// 2. Call the function
fetchData();
}, []); // [] -> Only calls once
// You might want to include dependencies here if you need to refetch based on props or state changes
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}4. Labs
Lab 1: Active Window Listener (The “Responsive” Spy)
Instead of just displaying boring numbers, let’s create a “Floating Debug Widget” with a Glassmorphism style. This widget will change color based on the screen size (Mobile/Tablet/Desktop).

💡 Hint:
- Use
window.addEventListener('resize')to update thewidth/heightstate.- Mandatory: Include
removeEventListenerin thecleanup functionto prevent memory leaks.- Use
if/elselogic or a ternary operator to change thebackgroundcolor (bg-red-500,bg-green-500) according to breakpoints (e.g.,< 640pxfor mobile).
Lab 2: English Dictionary (Debounce & API)
Build a sophisticated English-English dictionary.
- Features: Look up vocabulary, pronunciation (Audio), display word types (
noun,verb), and examples. - Techniques:
useEffectwith Debounce to search when the user stops typing, handle Audio API.

💡 Hint:
- Free API:
https://api.dictionaryapi.dev/api/v2/entries/en/<word>- Implement search Debounce (600ms) to avoid spamming the API on every keystroke.
- Professionally display Loading (Skeleton) and Not Found (404)
states.
5. Summary
- The Dependency Array is the key to controlling the
effect’s lifecycle. - The Cleanup Function is where you clean up “junk” (event listeners, timers) to protect memory.
- When calling an API in
useEffect:- Create an
async functioninside theeffect. - Always handle 3
states:loading,data,error. - Pay attention to using Debounce (
setTimeout/clearTimeout) when calling an API based on keystroke events.
- Create an