The useEffect hook is one of the most powerful and essential hooks in React. It allows you to perform side effects in function components, such as data fetching, subscriptions, or manually changing the DOM. Understanding how to use useEffect effectively can greatly enhance your ability to build dynamic and responsive React applications. This chapter will take you from the basics to advanced usage of useEffect, ensuring a comprehensive understanding.
useEffect
is a hook that runs side effects in your function components. Side effects include anything that affects something outside the scope of the function being executed, like fetching data from an API, setting up a subscription, or manually manipulating the DOM.
The basic usage of useEffect
involves two arguments: a function that contains the side effect code, and an optional array of dependencies that determine when the effect should re-run.
import React, { useEffect } from 'react';
function MessageLogger() {
useEffect(() => {
console.log('Component mounted');
}, []);
return Check the console;
}
export default MessageLogger;
[]
means the effect runs only once after the initial render (componentDidMount equivalent).
Check the console
In the console, you will see “Component mounted” logged once.
The dependencies array is crucial for controlling when the effect runs. If you omit the array, the effect runs after every render. You can also return a cleanup function from the effect to clean up resources.
import React, { useState, useEffect } from 'react';
function DocumentTitle() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return (
Count: {count}
);
}
export default DocumentTitle;
count
changes.useEffect
runs after every render where count
has changed.
Output :
Count: 0
When you click “Increment”, the document title updates to reflect the new count.
import React, { useState, useEffect } from 'react';
function Timer() {
const [seconds, setSeconds] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setSeconds((prevSeconds) => prevSeconds + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return Seconds: {seconds};
}
export default Timer;
seconds
every second.clearInterval
stops the interval when the component unmounts.
Output :
Seconds: 0
The “Seconds” value increases by 1 every second.
import React, { useState, useEffect } from 'react';
function DataFetcher() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then((response) => response.json())
.then((json) => {
setData(json);
setLoading(false);
});
}, []);
if (loading) {
return Loading...;
}
return (
{data.title}
{data.body}
);
}
export default DataFetcher;
setLoading
updates the loading state, and setData
stores the fetched data.
Output:
Post Title
Post body content...
The component initially shows “Loading…” and then displays the fetched data.
You can use multiple useEffect
hooks in a single component to handle different side effects.
import React, { useState, useEffect } from 'react';
function MultipleEffects() {
const [count, setCount] = useState(0);
const [name, setName] = useState('');
useEffect(() => {
console.log('Count has changed:', count);
}, [count]);
useEffect(() => {
console.log('Name has changed:', name);
}, [name]);
return (
Count: {count}
setName(e.target.value)}
placeholder="Enter your name"
/>
);
}
export default MultipleEffects;
useEffect
logs a message when count
changes.useEffect
logs a message when name
changes.
Output:
Count: 0
Clicking “Increment” or typing in the input field triggers the respective effects.
Be mindful of dependencies to avoid unnecessary re-renders.
import React, { useState, useEffect } from 'react';
function DependencyExample() {
const [count, setCount] = useState(0);
const [text, setText] = useState('');
useEffect(() => {
console.log('Effect with count:', count);
}, [count]);
return (
Count: {count}
setText(e.target.value)}
placeholder="Type something"
/>
);
}
export default DependencyExample;
count
changes, not when text
changes, optimizing performance.Ensure that effects only run when necessary.
import React, { useState, useEffect } from 'react';
function ConditionalEffect() {
const [isVisible, setIsVisible] = useState(true);
useEffect(() => {
if (isVisible) {
console.log('Component is visible');
}
}, [isVisible]);
return (
);
}
export default ConditionalEffect;
isVisible
changes, preventing unnecessary console logs.In this example, we’ll create a simple React app that fetches data from an API and displays it. We’ll use useEffect
to fetch the data when the component mounts.
import React, { useState, useEffect } from 'react';
function App() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts/1')
.then((response) => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then((json) => {
setData(json);
setLoading(false);
})
.catch((error) => {
setError(error);
setLoading(false);
});
}, []);
if (loading) {
return Loading...;
}
if (error) {
return Error: {error.message};
}
return (
{data.title}
{data.body}
);
}
export default App;
data
is initialized as null
.loading
is initialized as true
.error
is initialized as null
.[]
).fetch
function is used to get data from the API.data
state, and loading
is set to false
.error
state, and loading
is set to false
.loading
is true
, it displays “Loading…”.error
is not null
, it displays the error message.When you run this component, you’ll see “Loading…” initially. Once the data is fetched, it will display the title and body of the post fetched from the API. If there’s an error, it will display the error message.
In this example, we’ll create a React app that allows users to toggle between light mode and dark mode. We’ll use useEffect
to update the document’s body class based on the current mode.
import React, { useState, useEffect } from 'react';
import './App.css'; // Assume this file contains styles for light and dark modes
function App() {
const [isDarkMode, setIsDarkMode] = useState(false);
useEffect(() => {
if (isDarkMode) {
document.body.classList.add('dark-mode');
} else {
document.body.classList.remove('dark-mode');
}
// Cleanup function to remove class when component unmounts
return () => {
document.body.classList.remove('dark-mode');
};
}, [isDarkMode]);
return (
{isDarkMode ? 'Dark Mode' : 'Light Mode'}
);
}
export default App;
body {
background-color: white;
color: black;
}
.dark-mode {
background-color: black;
color: white;
}
.App {
text-align: center;
margin-top: 50px;
}
button {
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
}
isDarkMode
is initialized as false
.isDarkMode
changes.isDarkMode
is true
, the dark-mode
class is added to the document’s body.isDarkMode
is false
, the dark-mode
class is removed.dark-mode
class is removed if the component unmounts.isDarkMode
state between true
and false
.When you run this component, you’ll see a button labeled “Toggle Mode” and a heading displaying either “Light Mode” or “Dark Mode” depending on the current state. Clicking the button toggles the mode and updates the document’s body class, changing the background color and text color accordingly.
The useEffect hook is a cornerstone of React's functionality for managing side effects in functional components. By understanding its basics, dependencies, cleanup functions, and advanced use cases, you can effectively manage side effects in your React applications. This chapter provided an in-depth exploration of useEffect, from simple logging to complex data fetching and performance optimization. Mastering useEffect will significantly enhance your ability to create dynamic, responsive, and efficient React applications.Happy coding !❤️