The useEffect Hook is one of the most commonly used and, at times, most challenging Hooks in React. It allows you to run side effects in your functional components, enabling powerful features such as data fetching, subscribing to external services, and manipulating the browser DOM (e.g., playing audio or video).
This tutorial will walk you through the fundamental ideas of the useEffect Hook, exploring its usage, real world examples, folder structure guidelines, and best practices for new developers and curious learners.
In React, your components can handle three types of logic:
A React component's primary job during rendering is to produce the same output (JSX) for the same input (state, props, context). We say this is pure rendering. Because rendering must remain pure, any changes or "side effects" triggered by the component can't happen during the render itself. Instead, React categorizes them as either:
The useEffect Hook is precisely how you implement these side effects in a functional component.
The useEffect Hook runs a piece of code after React has updated the DOM (i.e., after the component is visible on the screen). This allows your component to synchronize with external systems, including:
document.title)You can have multiple useEffect calls in a single component, one for each distinct "effect." This keeps your code more organized than trying to combine all side effects into one large Hook.
To use useEffect, you import it from React in your functional component, and then call it at the top level of that component:
import React, { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// This code runs after the component is rendered
console.log('MyComponent has just rendered to the screen!');
});
return <div>Hello from MyComponent</div>;
}
One vital detail: useEffect does not run during the render cycle. Instead, once rendering is complete, React calls your effect function in the background, ensuring the render remains pure.
If you're using React.StrictMode in development, you'll notice your effect runs twice upon initial load.
StrictMode intentionally renders components twice to confirm they are pure. If you remove the
<React.StrictMode> wrapper, your effect will run only once in production.
Let’s do a simple exercise demonstrating how useEffect fires after the component is on screen. This will also help clarify the order in which logs appear.
import React, { useEffect } from 'react';
function UseEffectExample() {
console.log('Inside UseEffectExample - during render');
useEffect(() => {
console.log('Inside UseEffectExample - after render');
});
return <h1>Hello from UseEffectExample</h1>;
}
export default UseEffectExample;
import React from 'react';
import UseEffectExample from './useeffect_example';
function App() {
return (
<div>
<UseEffectExample />
</div>
);
}
export default App;
Next, run your development server with npm start (or yarn start). Check your console:
If StrictMode is enabled, these may appear twice in your console. That is normal in development to help catch side effect bugs.
Imagine building a car on an assembly line (the render phase). The car must remain consistent and correct, so it strictly adheres to an unchanging plan. Meanwhile, any extra tasks like adding fuel or painting the brand logo happen after it leaves the main assembly line (the effect phase). If we did them during the assembly, it might compromise the purity of the building process. Similarly, useEffect ensures side tasks happen only after the "pure" production (render) is done.
Suppose you have a user profile page that needs to fetch user data from an API. The useEffect might look like this:
function UserProfile() {
const [user, setUser] = useState(null);
useEffect(() => {
async function fetchUser() {
const res = await fetch('/api/current-user');
const data = await res.json();
setUser(data);
}
fetchUser();
}, []); // dependency array empty => runs once on component mount
if (!user) {
return <div>Loading...</div>;
}
return <div>Hello, {user.name}!</div>;
}
The code runs after the component is on screen, fetching user data in the background. Once the data arrives, your local state updates, triggering another render to display the user info. This is a typical pattern in React apps for data synchronization with an external server.
useEffect is essential anytime you want your React component to do something that can’t be done purely during render. This includes:
document.titlesetInterval or setTimeoutThe useEffect hook has two key features we haven’t deeply covered yet:
You will explore these in detail in further readings or lessons. They’re critical for managing repeating, updating, or unsubscribing tasks.
Additionally, consider that useEffect is a broad solution. If your effect is specifically about fetching data or using an external library, you might investigate specialized hooks or libraries that further simplify these tasks.
React.StrictMode verifying your side effects. This does not happen in production, but it helps you catch code that relies on side effect ordering incorrectly.
Congratulations! You have taken a big step toward understanding React’s useEffect Hook. Remember the key ideas:
By mastering useEffect and its dependency array/cleanup function, you’ll be able to build dynamic, interactive applications that stay synchronized with the outside world while preserving React’s purity and performance.