
Day 10: Understanding useEffect React Hook
In today’s React learning series, we will deep dive into useEffect React Hook. So far in this React series, we’ve learned how to manage state with useState
, render lists, handle events, and more. But apps don’t just display data — they also need to fetch data, update the document title, listen to events, and clean up after themselves.
This is where useEffect
comes in.
What is useEffect?
useEffect
is a built-in React Hook that lets you perform side effects in functional components.
👉 But what’s a side effect?
A side effect is anything that affects something outside the component. Examples:
- Fetching data from an API
- Subscribing to an event (like key presses)
- Updating the document title
- Setting up timers (
setTimeout
,setInterval
)
Before Hooks, these were only possible in class components using lifecycle methods (componentDidMount
, componentDidUpdate
, componentWillUnmount
). With useEffect
, we can now do it directly in functional components.
Basic Syntax
useEffect(() => {
// code that runs after render
});
- The function inside
useEffect
runs after every render by default. - We can control when it runs using the dependency array (explained later).
Example 1: Updating Document Title
import React, { useState, useEffect } from "react";
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click Me</button>
</div>
);
}
export default Counter;
👉 Every time count
changes, React re-renders and the effect updates the document title.
Controlling When useEffect Runs
By default, useEffect
runs after every render. But often, you don’t want that. That’s where the dependency array comes in.
Run Once (on Mount)
useEffect(() => {
console.log("Component mounted!");
}, []);
- An empty dependency array
[]
means the effect runs only once (when the component first renders). - Useful for things like API calls or subscriptions.
Run When Certain State Changes
useEffect(() => {
console.log(`Count changed: ${count}`);
}, [count]);
- Here, the effect runs only when
count
changes. - This improves performance by preventing unnecessary runs.
Cleanup with useEffect
Sometimes, side effects need to be cleaned up — for example, when removing event listeners or clearing timers.
Example: Timer with Cleanup
import React, { useEffect, useState } from "react";
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setSeconds(prev => prev + 1);
}, 1000);
// Cleanup function
return () => clearInterval(interval);
}, []);
return <h2>Timer: {seconds} seconds</h2>;
}
export default Timer;
👉 Without cleanup, multiple intervals would run and cause bugs. Cleanup ensures we “unsubscribe” when the component unmounts.
Example: Fetching Data
import React, { useEffect, useState } from "react";
function Users() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then(res => res.json())
.then(data => setUsers(data));
}, []); // run once on mount
return (
<div>
<h2>User List</h2>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
export default Users;
👉 This is one of the most common use cases of useEffect
— fetching data when a component loads.
Common Mistakes with useEffect
- Missing dependencies: Always include variables you use inside
useEffect
in the dependency array.
useEffect(() => {
console.log(count);
}, []); // ❌ count is missing from dependencies
- Overusing useEffect
Not everything needsuseEffect
. For example, simple calculations can be done directly in render. - Forgetting cleanup
Not cleaning up event listeners or intervals can cause memory leaks.
Summary
useEffect
lets you perform side effects in React functional components.- By default, it runs after every render.
- Use the dependency array to control when it runs:
[]
→ run once (on mount).[value]
→ run whenvalue
changes.
- Always use cleanup when needed (timers, listeners).
- Perfect for tasks like fetching data, updating the DOM, or handling subscriptions.
What’s Next?
Now that you know the basics of useEffect
, in Day 11 we’ll explore useRef
— a hook that helps us access DOM elements directly and persist values across renders without causing re-renders.