The useEffect
hook is a fundamental part of React that allows you to perform side effects in function components. Side effects include data fetching, subscriptions, manual DOM manipulations, and more. Here’s an in-depth look at how useEffect
works and how to use it effectively.
Importing useEffect
First, you need to import useEffect
from the react
library:
import React, { useEffect } from 'react';
Basic Usage
The useEffect
hook takes a function as its argument. This function will run after the component renders. By default, useEffect
runs after every render.
useEffect(() => {
// Code to run after render
});
Dependencies Array
You can control when useEffect
runs by providing a dependencies array as the second argument. The effect will only re-run if one of the dependencies has changed.
- No dependencies array: Runs after every render.
- Empty dependencies array: Runs only once after the initial render.
- With dependencies: Runs when any of the dependencies change.
useEffect(() => {
// Code to run after render
}, [dependency1, dependency2]);
Cleaning Up Side Effects
If your effect creates resources that need to be cleaned up (like subscriptions or timers), you can return a cleanup function from the effect. This cleanup function runs before the component unmounts and before the effect runs again.
useEffect(() => {
// Code to run after render
return () => {
// Cleanup code
};
}, [dependency]);
Remember that state updates are asynchronous, so avoid assuming immediate changes after a state update.
The useEffect
hook is an essential tool for handling side effects in functional components. It provides a way to synchronize your component with external systems, manage subscriptions, perform data fetching, and more. By mastering useEffect
, you can create more dynamic and responsive React applications.
Example
use the public JSONPlaceholder API to fetch a list of posts.
App.js:
import React, { useEffect, useState } from 'react';
const PostList = () => {
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchPosts = async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
setPosts(data);
} catch (error) {
setError(error.message);
} finally {
setLoading(false);
}
};
fetchPosts();
// Cleanup function (if needed)
return () => {
console.log('Cleanup function called');
};
}, []); // Empty dependency array means this effect runs once after the initial render
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error}</p>;
return (
<div>
<h1>Posts</h1>
<ul>
{posts.map(post => (
<li key={post.id}>
<h2>{post.title}</h2>
<p>{post.body}</p>
</li>
))}
</ul>
</div>
);
};
export default function App() {
return (
<PostList></PostList>
);
}
Result: