Frontend DevelopmentuseEffect Hook: The What, When, and Where of Side-Effects & Cleanup
When you're building a React application, you often need to perform tasks that don't directly involve rendering the UI. These tasks, such as fetching data from an API, setting up a subscription, or manually changing the DOM, are called side effects. The useEffect hook is a powerful tool in React that allows you to manage these side effects in function components. What is useEffect hook? useEffect is a built-in React hook that lets you "hook into" the component lifecycle. It's a way to tell React to run a function after every render, or after a specific state or prop has changed. Think of it as a function that performs actions that are "outside" the normal flow of rendering a component.It takes two arguments: - a function containing the side effect code and - an optional dependency array.The basic syntax looks like this:import React, { useEffect } from 'react';
useEffect(() => {
// Your side effect code goes here
}, [dependencyArray]);
By employing useEffect you are signaling to React that your component needs to execute specific actions after rendering. React keeps track of the function you supply (our effects) and runs it following the DOM updates.This feature allows you to execute various operations such as updating the document title, fetching data, or making API calls.When and Where to Use It The useEffect hook is used for any kind of side effect. Common use cases include:Data Fetching: Making an API call to get data and then storing it in state.Subscriptions: Setting up a subscription to an external service, like a WebSocket.Manually Interacting with the DOM: Directly manipulating the document, for example, to set the title of the page.Timers: Setting up and clearing timers, such as setTimeout or setInterval. The Dependency Array: Controlling the Effect's Behavior The second argument to useEffect, the dependency array, is crucial for controlling when your effect runs. The effect will re-run only when a value in this array changes. This prevents the effect from running on every single render, which can lead to performance issues or infinite loops. 1. No Dependency ArrayIf you omit the dependency array, the effect will run after every single render of the component.useEffect(() => {
// This will run after every render
console.log('Component rendered or state updated');
});This is generally not recommended for performance-sensitive operations like data fetching, as it will cause a new request on every render. 2. Empty Dependency Array ([])An empty dependency array tells React that the effect should only run once, after the initial render of the component. This is the ideal use case for one-time setup effects, like an initial API call.useEffect(() => {
// This will only run once, on component mount
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => console.log(data));
}, []);3. Populated Dependency Array ([prop1, state1])When you include variables (props or state) in the dependency array, the effect will re-run whenever any of those variables change. This is perfect for effects that depend on specific values.const [userId, setUserId] = useState(1);
const [userData, setUserData] = useState(null);
useEffect(() => {
// This will re-run whenever userId changes
fetch(`https://api.example.com/users/${userId}`)
.then(response => response.json())
.then(data => setUserData(data));
}, [userId]);In this example, the effect will fetch new user data only when the userId state variable is updated, not on every render. The Cleanup FunctionSome side effects, like subscriptions or timers, need to be "cleaned up" to prevent memory leaks. The useEffect hook provides a way to do this by allowing you to return a function from your effect callback. This returned function is the cleanup function. React will run the cleanup function in two scenarios: - Just before the effect re-runs due to a dependency change. - When the component is unmounted (removed from the DOM).Here's an example of using a cleanup function to unsubscribe from a service.import React, { useEffect } from 'react';
import ChatAPI from './ChatAPI';
function ChatRoom({ roomId }) {
useEffect(() => {
// This function sets up the subscription
ChatAPI.subscribeToChat(roomId);
// This function is the cleanup function
return () => {
ChatAPI.unsubscribeFromChat(roomId);
};
}, [roomId]); // Re-subscribe if the roomId changes
return <h1>Welcome to the chat room!</h1>;
}In this code, the ChatAPI.subscribeToChat function is called when the component mounts and whenever the roomId prop changes. The function returned from useEffect will then unsubscribe from the previous chat before the new subscription is set up, and also when the ChatRoom component unmounts. This is a crucial pattern for managing external resources in your React components. Points to Remember: - Use the cleanup function to free up the resources and for cleanups related to the effects. - The cleanup function runs before the component unmounts and before the effect re-run due to dependency change. Conclusion The useEffect hook in React is powerful feature for managing the side effects in functional components. It’s ability to handle post render actions, combined with a customizable dependency array and cleanup mechanism, make it an essential for efficient and effective React development.
5 min read•Aug 21, 2026