React Lifecycle and useEffect Hook
Introduction
So far, you’ve learned how to build components, manage state, handle events, render lists, and work with forms. Now, it’s time to understand what happens behind the scenes when components load, update, and are removed.
In React, every component goes through a lifecycle — a series of stages from creation to removal. In functional components, we manage these lifecycle behaviors using the useEffect hook.
What Is a Component Lifecycle?
A component lifecycle refers to the different phases a component goes through:
Mounting – when the component is created and added to the screen
Updating – when state or props change
Unmounting – when the component is removed from the screen
In older class components, special lifecycle methods handled these phases. In modern React, the useEffect hook replaces most of that functionality.
What is the useEffect Hook?
The useEffect hook lets you perform side effects in functional components. Side effects are operations that interact with things outside the component, such as:
Fetching data from an API
Setting timers
Updating the document title
Subscribing to events
Basic syntax:
import { useEffect } from "react";
useEffect(() => {
// Side effect code here
});
Running useEffect on Every Render
If you don’t provide a dependency array, useEffect runs after every render.
useEffect(() => {
console.log("Component rendered");
});
This runs after the component first loads and after every update.
Running useEffect Only Once (On Mount)
To run an effect only when the component first loads, pass an empty dependency array.
useEffect(() => {
console.log("Component mounted");
}, []);
This behaves like the mounting phase.
Running useEffect When Data Changes
You can control when the effect runs by adding dependencies.
useEffect(() => {
console.log(`Count changed: ${count}`);
}, [count]);
Now, the effect runs only when count changes.
Cleaning Up Effects (Unmounting)
Some effects need cleanup, such as timers or subscriptions. useEffect can return a cleanup function.
useEffect(() => {
const timer = setInterval(() => {
console.log("Running...");
}, 1000);
return () => {
clearInterval(timer);
console.log("Component unmounted");
};
}, []);
The returned function runs when the component unmounts.
Example: Fetching Data
import { useState, useEffect } from "react";
function Users() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((res) => res.json())
.then((data) => setUsers(data));
}, []);
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
The data loads once when the component mounts.
Common Mistakes to Avoid
Forgetting the dependency array
Causing infinite loops by updating the state inside useEffect without dependencies
Not cleaning up timers or subscriptions
Summary
In this chapter, you learned how React components go through lifecycle phases and how the useEffect hook manages side effects. You saw how to run effects on mount, on updates, and how to clean up resources. This knowledge helps you handle real-world tasks such as fetching data and managing timers efficiently.