Skip to main content

useEffect Hook — Side Effects

Level: Beginner

ℹ️ What You'll Learn
  • What side effects are and why they need useEffect
  • Running effects on component mount (empty dependency array)
  • Running effects when props/state change (dependency array)
  • Cleaning up effects (return cleanup function)
  • Common effect patterns: fetching data, timers, subscriptions
  • Avoiding infinite loops with dependencies
  • useEffect for SMS screens: loading students, polling attendance, syncing with API
  • Best practices and gotchas

Why This Matters

useEffect Hook — Side Effects is part of building maintainable React applications. You will use it when creating student dashboards, forms, tables, API-connected screens, routing flows, and reusable UI components.

useEffect handles side effects: fetching data, setting timers, subscribing to events, updating the document title.

The Problem

Beginners often write React code that works for a small demo but becomes difficult when data, forms, API calls, and reusable components grow. This lesson explains useEffect Hook — Side Effects in a way that helps you build predictable UI for real .NET Web API projects.

What is a Side Effect?

Side effects are things that happen outside the component: API calls, timers, DOM manipulation.

function StudentDetail({ studentId }) {
// ❌ Wrong — runs every render
fetch(`/api/students/${studentId}`)
.then(r => r.json())
.then(data => console.log(data));

return <div>Loading...</div>;
}

Calling fetch() on every render makes infinite API calls.

useEffect runs the effect at specific times, not every render.

Basic useEffect Syntax

useEffect(() => {
// Effect runs here
}, [dependencies]);
  • Effect function: runs when dependencies change
  • Dependency array: what triggers the effect

useEffect Patterns

Pattern 1: Run Once (On Mount)

function StudentDetail({ studentId }) {
const [student, setStudent] = useState(null);

useEffect(() => {
// Fetch when component mounts
fetch(`/api/students/${studentId}`)
.then(r => r.json())
.then(data => setStudent(data));
}, []); // Empty dependency = run once

if (!student) return <p>Loading...</p>;
return <div>{student.name}</div>;
}

Empty dependency array [] means: run once when component first appears.

Pattern 2: Run on Prop Change

function StudentDetail({ studentId }) {
const [student, setStudent] = useState(null);

useEffect(() => {
fetch(`/api/students/${studentId}`)
.then(r => r.json())
.then(data => setStudent(data));
}, [studentId]); // Re-run when studentId changes

if (!student) return <p>Loading...</p>;
return <div>{student.name}</div>;
}

When studentId prop changes, the effect runs again with new ID.

Pattern 3: Run Every Render

function StudentCounter() {
const [count, setCount] = useState(0);

useEffect(() => {
document.title = `Clicked ${count} times`;
}); // No dependency array = run every render

return <button onClick={() => setCount(count + 1)}>Click me</button>;
}

Title updates every time component re-renders.

Pattern 4: Cleanup Function

Some effects need cleanup: unsubscribe, cancel requests, clear timers.

function StudentTimer() {
const [seconds, setSeconds] = useState(0);

useEffect(() => {
// Start timer
const interval = setInterval(() => {
setSeconds(s => s + 1);
}, 1000);

// Cleanup: stop timer when component unmounts
return () => clearInterval(interval);
}, []);

return <p>Elapsed: {seconds}s</p>;
}

The cleanup function runs when component unmounts or before re-running the effect.

Real Examples

Example 1: Fetch Student Data

function StudentProfile({ studentId }) {
const [student, setStudent] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
let isMounted = true; // Prevent state updates after unmount

const fetchStudent = async () => {
try {
const response = await fetch(`/api/students/${studentId}`);
const data = await response.json();

if (isMounted) {
setStudent(data);
setError(null);
}
} catch (err) {
if (isMounted) {
setError(err.message);
}
} finally {
if (isMounted) {
setLoading(false);
}
}
};

fetchStudent();

// Cleanup
return () => {
isMounted = false;
};
}, [studentId]);

if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return <div>{student.name}</div>;
}

Example 2: Fetch Multiple Resources

function StudentDashboard({ studentId }) {
const [student, setStudent] = useState(null);
const [marks, setMarks] = useState([]);
const [attendance, setAttendance] = useState([]);

useEffect(() => {
// Fetch all data when studentId changes
Promise.all([
fetch(`/api/students/${studentId}`).then(r => r.json()),
fetch(`/api/students/${studentId}/marks`).then(r => r.json()),
fetch(`/api/students/${studentId}/attendance`).then(r => r.json())
]).then(([studentData, marksData, attendanceData]) => {
setStudent(studentData);
setMarks(marksData);
setAttendance(attendanceData);
});
}, [studentId]);

return (
<div>
<h1>{student?.name}</h1>
<p>Marks: {marks.length}</p>
<p>Attendance: {attendance.length}%</p>
</div>
);
}

Example 3: Listen to Browser Events

function StudentWindowSize() {
const [width, setWidth] = useState(window.innerWidth);

useEffect(() => {
const handleResize = () => setWidth(window.innerWidth);

// Subscribe to resize event
window.addEventListener('resize', handleResize);

// Cleanup: unsubscribe
return () => window.removeEventListener('resize', handleResize);
}, []);

return <p>Width: {width}px</p>;
}
function StudentSearch() {
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);

useEffect(() => {
// Delay search to avoid too many requests
const timer = setTimeout(() => {
if (query.length > 0) {
fetch(`/api/students/search?q=${query}`)
.then(r => r.json())
.then(data => setResults(data));
} else {
setResults([]);
}
}, 500); // Wait 500ms after typing stops

// Cleanup: cancel previous search
return () => clearTimeout(timer);
}, [query]);

return (
<>
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search students..."
/>
<ul>
{results.map(student => (
<li key={student.id}>{student.name}</li>
))}
</ul>
</>
);
}

Dependency Array Rules

❌ Wrong

// Running on every render (expensive)
useEffect(() => {
fetchData();
}); // No dependency array

// Running once (doesn't update when prop changes)
useEffect(() => {
fetchStudent(studentId);
}, []); // Missing studentId

// Not running but should
useEffect(() => {
fetch(`/api/students/${studentId}`);
}, []); // Should include studentId

✓ Correct

// Run once on mount
useEffect(() => {
fetchInitialData();
}, []);

// Run when studentId changes
useEffect(() => {
fetchStudent(studentId);
}, [studentId]);

// Run when either changes
useEffect(() => {
fetchTeacher(teacherId);
}, [teacherId, schoolId]);

Multiple Effects

One component can have multiple effects:

function StudentDetail({ studentId }) {
// Effect 1: Fetch student data
useEffect(() => {
fetch(`/api/students/${studentId}`)
.then(r => r.json())
.then(data => setStudent(data));
}, [studentId]);

// Effect 2: Fetch marks
useEffect(() => {
fetch(`/api/students/${studentId}/marks`)
.then(r => r.json())
.then(data => setMarks(data));
}, [studentId]);

// Effect 3: Update document title
useEffect(() => {
document.title = `${student?.name} - Details`;
}, [student]);

return <div>{student?.name}</div>;
}

Each effect handles one concern.

Key Takeaways

  • useEffect runs side effects at specific times
  • [] = run once on mount
  • [dependency] = run when dependency changes
  • Return cleanup function to unsubscribe/cancel
  • Multiple effects are fine
  • Next: Lists and rendering arrays
⚠️ Common Mistakes
  1. Forgetting dependency array — Causes infinite loops
  2. Missing dependencies — Effect doesn't re-run when needed
  3. Not cleaning up — Memory leaks from timers/events
  4. Async in effect directly — Use async function inside effect
  5. Updating state inside cleanup — Can cause warnings
💡 useEffect Debugging

Add logging to understand when effects run:

useEffect(() => {
console.log('Effect running with studentId:', studentId);
fetchStudent(studentId);
return () => {
console.log('Cleanup running');
};
}, [studentId]);
🤖Use AI to Learn Faster

Use ChatGPT, Claude, or Copilot to go deeper on useEffect Hook. Try these prompts:

  • "When should useEffect run vs. the render function?"
  • "What does an empty dependency array mean?"
  • "Why do you need cleanup functions?"
  • "Quiz me on useEffect patterns"

💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.

Quick Definitions

  • useEffect Hook — Side Effects - The main React concept explained in this lesson.
  • Component - A reusable function that returns UI.
  • Props/state/effects - Core React ideas used to pass data, remember data, and run side effects.
  • Real project usage - How this appears in forms, dashboards, routes, and API-connected pages.

Common Mistakes

  • Copying React code without understanding data flow
  • Mutating arrays or objects directly instead of creating new values
  • Forgetting keys, dependencies, loading states, or error states where needed
  • Putting too much logic in one component
  • Not testing the screen with realistic School Management System data

Practice Task

Create a small React example using useEffect Hook — Side Effects. Keep it focused on one School Management System screen.

Suggested practice:

  1. Build a small component or page for students, attendance, marks, or fees.
  2. Pass realistic data into the component.
  3. Add one success state and one empty/error state where relevant.
  4. Explain the data flow in your own words.
  5. Rebuild the same example once without looking at the article.

Quick Revision

QuestionAnswer
What is the main idea?Understand and apply useEffect Hook — Side Effects in React.
Where is it used?Student dashboards, forms, tables, routes, and API-connected screens.
What should beginners focus on?Clear components, predictable data flow, and small examples.
What is the best debugging habit?Check props, state, render output, and browser console step by step.
🎯 How would you explain useEffect Hook — Side Effects in an interview?

useEffect Hook — Side Effects is a React concept used to build clear, reusable, and predictable user interfaces. I would explain the problem it solves, show a small component example, and mention a common mistake beginners should avoid.

🎯 Where is this used in a real React project?

It is used in screens like student lists, admission forms, attendance dashboards, marks reports, routing pages, and API-connected admin panels.

Next Article

Rendering Lists and Keys ->

nexcoding.in