React useEffect Hook
# React useEffect Hook
1. Introduction
React components are supposed to be "pure functions." They take props and state, and they return JSX. However, real applications need to do things that reach outside the component: fetching data from an API, talking to a database, manipulating the DOM, or setting up a timer. In React, these are called Side Effects. To handle them safely, we use theuseEffect Hook.
2. Learning Objectives
By the end of this chapter, you will be able to:- Understand what a "Side Effect" is in React.
-
Use
useEffectto execute code after a component renders.
- Control exactly *when* an effect runs using the Dependency Array.
- Clean up effects to prevent memory leaks.
3. Beginner-Friendly Explanations
What is useEffect?
useEffect tells React: *"Hey, after you finish putting the HTML on the screen, please run this block of code."*
This is crucial because you don't want a heavy API fetch to freeze the screen while it loads. You want the screen to load immediately (maybe showing a spinner), and *then* the effect fetches the data in the background.
4. Syntax Explanation
Step 1: The Basic Setup
useEffect takes a callback function as its first argument. By default, it runs after every single render.
``jsx id="ch13_ex1"
import { useState, useEffect } from 'react';
function Logger() { const [count, setCount] = useState(0);
// This will run when the component first loads, AND every time 'count' changes. useEffect(() => { console.log("The component rendered! Count is now: " + count); document.title = Clicked ${count} times`; });
return <button onClick={() => setCount(count + 1)}>Click Me</button>; }
jsx id="ch13_ex2" useEffect(() => { console.log("I only run once when the page loads!"); }, []); // Empty array!
jsx id="ch13_ex3" const [searchQuery, setSearchQuery] = useState("");
useEffect(() => { console.log("The user typed a new search query! Let's fetch new data."); }, [searchQuery]); // Only runs when searchQuery changes!
jsx id="ch13_ex4" useEffect(() => { const timer = setInterval(() => console.log("Tick"), 1000);
// Cleanup function: runs when the component unmounts return () => { clearInterval(timer); }; }, []);
jsx id="ch13_proj1" // Example React component import React, { useState, useEffect } from 'react';
export default function QuoteOfTheDay() { const [quote, setQuote] = useState(""); const [author, setAuthor] = useState(""); const [isLoading, setIsLoading] = useState(true);
// Function to fetch data const fetchQuote = async () => { setIsLoading(true); try { // Using a free dummy API for quotes const response = await fetch('https://dummyjson.com/quotes/random'); const data = await response.json(); setQuote(data.quote); setAuthor(data.author); } catch (error) { setQuote("Failed to fetch quote."); } setIsLoading(false); };
// Run ONCE when component mounts useEffect(() => { fetchQuote(); }, []); // <-- Empty array is critical here!
return ( <div className="flex items-center justify-center min-h-screen bg-gray-100 p-4"> <div className="bg-white max-w-lg w-full p-8 rounded-2xl shadow-xl text-center"> <h1 className="text-xl font-bold text-gray-500 uppercase tracking-widest mb-6">Inspiration</h1> {isLoading ? ( <div className="animate-pulse flex flex-col space-y-4"> <div className="h-4 bg-gray-200 rounded w-3/4 mx-auto"></div> <div className="h-4 bg-gray-200 rounded w-1/2 mx-auto"></div> </div> ) : ( <> <p className="text-2xl italic text-gray-800 mb-6">"{quote}"</p> <p className="text-lg font-semibold text-blue-600">- {author}</p> </> )}
<button
onClick={fetchQuote}
disabled={isLoading}
className="mt-8 bg-black text-white px-6 py-2 rounded-lg font-medium hover:bg-gray-800 disabled:bg-gray-400 transition"
>
Get Another Quote
</button>
</div>
</div>
);
}
``
11. Coding Challenges
Challenge 1: Create a WindowWidth component. Use useEffect to attach a resize event listener to the window object. Update a state variable with the current window width. Don't forget the cleanup function to remove the event listener!
12. MCQs with Answers
Q1: What does an empty dependency array [] signify in useEffect?
A) The effect should never run.
B) The effect should run on every render.
C) The effect should run exactly once, when the component mounts.
*Answer: C*
Q2: How do you prevent memory leaks from interval timers inside
useEffect?
A) By deleting the timer variable.
B) By returning a cleanup function that calls clearInterval.
C) React automatically clears timers for you.
*Answer: B*
13. Interview Questions
-
Q: Explain what the Dependency Array does in
useEffect.
-
Q: What happens if you update state inside a
useEffect but forget to add a dependency array?
14. FAQs
Can I make the useEffect callback function async directly?
No, useEffect(async () => {}) is an anti-pattern and will cause issues. Instead, define an async function *inside* the effect and call it, exactly as we did in the Mini Project.
15. Summary
The useEffect hook is your bridge to the outside world. It allows functional components to perform side effects safely. By mastering the Dependency Array, you gain precise control over when your effects execute, preventing infinite loops and ensuring high-performance applications.
16. Next Chapter Recommendation
useEffect` effectively replaced the old "Lifecycle Methods" used in Class components. In Chapter 14: React Component Lifecycle Basics, we will briefly look at the theoretical concept of a component's lifecycle (Mounting, Updating, Unmounting) to better understand exactly what React is doing under the hood.