The useEffect
hook in React allows you to perform side effects in function components. It's a close equivalent to lifecycle methods in class components, like componentDidMount
, componentDidUpdate
, and componentWillUnmount
.
useEffect
RendersFirst, import useEffect
from React and create a useEffect
function.
import { useEffect } from 'react';
useEffect(() => {
// This will run once when the component mounts
return () => {
// This will run when the component unmounts
}
}, []);
When the dependency array ([]) is provided, the effect will run once when the component mounts. This is useful for tasks such as initializing data or setting up subscriptions that only need to happen once.
useEffect(() => {
// This will run once when the component mounts
return () => {
// This will run when the component unmounts
}
}, []);
If you forget to include the dependency array ([]
), the effect will run on every render, which can lead to performance issues and unintended behavior.
When no dependency array is provided, the effect will run after every render. This is useful for tasks that need to be executed on every render, regardless of the component's state or props.
useEffect(() => {
// This will run every time the component updates
return () => {
// Cleanup function
}
});
Omitting the dependency array will cause the effect to run after every render, which may lead to performance issues if not handled properly.
When a dependency array with specific values is provided, the effect will only run when one of those values changes. This is useful for tasks that depend on specific state or props values.
useEffect(() => {
// This will run when the 'count' variable changes
return () => {
// Cleanup function
}
}, [count]);
Including too many dependencies in the dependency array can cause the effect to run more often than necessary, which can impact performance.
useEffect
WorksWhen useEffect
is called, it first executes the effect function and then returns the cleanup function.
useEffect(() => {
console.log('outside');
return () => {
console.log('inside');
}
}, [count]);
Returning a cleanup function is crucial when dealing with subscriptions or event listeners to prevent memory leaks and unwanted side effects.
useEffect
Here's an example of how to use useEffect
to fetch data from an API.
import React, { useState, useEffect } from 'react';
const FetchDataExample = () => {
const [resource, setResource] = useState('posts');
const [data, setData] = useState([]);
useEffect(() => {
fetch(`https://jsonplaceholder.typicode.com/${resource}`)
.then(response => response.json())
.then(json => setData(json));
console.log(data);
}, [resource]);
return (
<>
<h1>{resource}</h1>
<button onClick={() => setResource('users')}>Users</button><br /><br />
<button onClick={() => setResource('comments')}>Comments</button><br /><br />
<button onClick={() => setResource('posts')}>Posts</button>
<div className="">
{data.map((val, index) => (
<p key={index}>{val.title}{val.name}</p>
))}
</div>
</>
);
};
export default FetchDataExample;
Be cautious with the dependencies of useEffect
when dealing with asynchronous operations. Failing to include dependencies like state setters can cause stale closures and bugs.
When defining a function that runs outside of the return, it may slow down the app. We use cleanup functions to clear these effects.
import React, { useState, useEffect } from 'react';
const ResizeExample = () => {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => {
setWidth(window.innerWidth);
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, [width]);
return (
<>
<h1>{width}</h1>
<button onClick={() => setWidth(window.innerWidth)}>{width}</button>
</>
);
};
export default ResizeExample;
Be mindful of the dependency array to avoid running the cleanup function and effect unnecessarily.
useEffect
Objective: Create a component that fetches data from an API and updates the component based on the data.
Create a FetchData Component:
Create a new file FetchData.jsx
and define a component that fetches data from an API.
// FetchData.jsx
import React, { useState, useEffect } from 'react';
const FetchData = () => {
const [data, setData] = useState([]);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(json => setData(json));
}, []);
return (
<div>
{data.map((post) => (
<p key={post.id}>{post.title}</p>
))}
</div>
);
};
export default FetchData;
Create a Resize Component:
Create a new file Resize.jsx
and define a component that updates the window width on resize.
// Resize.jsx
import React, { useState, useEffect } from 'react';
const Resize = () => {
const [width, setWidth] = useState(window.innerWidth);
useEffect(() => {
const handleResize = () => {
setWidth(window.innerWidth);
};
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
return (
<div>
<h1>{width}</h1>
</div>
);
};
export default Resize;
Combine Components in App:
Import and use both FetchData
and Resize
components in your App
component to see them in action.
// App.jsx
import React from 'react';
import FetchData from './FetchData';
import Resize from './Resize';
const App = () => {
return (
<>
<FetchData />
<Resize />
</>
);
};
export default App;
This task will help you practice using the useEffect
hook to manage side effects such as data fetching and window resize events in React components.