What is useEffect
in React?
useEffect
is a React hook that allows you to perform side effects in functional components. Side effects refer to operations that interact with the outside world, such as:
- Data fetching: Making API calls to retrieve data.
- Subscriptions: Listening for events (e.g., from WebSockets).
- Direct DOM manipulations: Changing the document title or manipulating third-party libraries that interact with the DOM.
- Timers: Setting up intervals or timeouts.
Basic Syntax
The useEffect
hook accepts two arguments:
- Effect Function: This function contains the code for your side effect.
- Dependency Array (optional): An array of dependencies that determine when the effect should run.
Syntax
useEffect(() => {
// Your side effect logic here
return () => {
// Cleanup logic (optional)
};
}, [dependencies]);
How useEffect
Works
- Initial Render: The effect runs after the component mounts for the first time.
- Updates: If the dependency array is provided, the effect will re-run whenever any value in that array changes.
- Cleanup: If your effect returns a function, that function is called when the component unmounts or before the effect runs again (for cleanup).
Detailed Examples
Example 1: Fetching Data
Here’s a common scenario where you might use useEffect
to fetch data from an API when a component mounts:
import React, { useState, useEffect } from 'react';
const DataFetcher = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const result = await response.json();
setData(result);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
fetchData();
}, []); // Empty array means this effect runs only on mount
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>Data</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
};
export default DataFetcher;
Example 2: Subscribing to Events
In this example, we’ll use useEffect
to subscribe to an event, such as a WebSocket connection:
import React, { useState, useEffect } from 'react';
const WebSocketComponent = () => {
const [messages, setMessages] = useState([]);
useEffect(() => {
const socket = new WebSocket('wss://example.com/socket');
socket.onmessage = (event) => {
setMessages((prevMessages) => [...prevMessages, event.data]);
};
// Cleanup function to close the socket connection
return () => {
socket.close();
};
}, []); // Run only on mount and unmount
return (
<div>
<h1>WebSocket Messages</h1>
<ul>
{messages.map((msg, index) => (
<li key={index}>{msg}</li>
))}
</ul>
</div>
);
};
export default WebSocketComponent;
Example 3: Document Title Update
Another common use case is updating the document title when a component mounts or when a specific state changes:
import React, { useState, useEffect } from 'react';
const TitleUpdater = () => {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]); // Run effect when count changes
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
};
export default TitleUpdater;
Example 4: Timer with Cleanup
In this example, we set up a timer that increments a count every second, and we clean up the timer when the component unmounts:
import React, { useState, useEffect } from 'react';
const Timer = () => {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCount((prevCount) => prevCount + 1);
}, 1000);
// Cleanup function to clear the timer
return () => clearInterval(timer);
}, []); // Empty array means this effect runs only on mount and unmount
return <div>Count: {count}</div>;
};
export default Timer;
Key Points to Remember
Dependency Array:
- If omitted, the effect runs after every render.
- An empty array (
[]
) means it runs only on mount and unmount. - Including variables in the array means the effect will run whenever those variables change.
Cleanup Function: Always return a cleanup function if your effect requires it (like unsubscribing from events or clearing timers).
Multiple Effects: You can use multiple
useEffect
hooks in a single component to handle different side effects independently.Performance Considerations: Be cautious with how many dependencies you include in the array, as changes to them will trigger the effect, potentially leading to performance issues.
Common Use Cases
- Fetching Data: Making HTTP requests to retrieve data when a component mounts.
- Event Listeners: Adding event listeners and cleaning them up.
- Animations: Triggering animations or transitions.
- Timers and Intervals: Setting up periodic updates or actions.
- Local Storage: Syncing state with local storage.
Conclusion
The useEffect
hook is a powerful tool in React that helps you manage side effects in functional components. By understanding its behavior and how to use it effectively, you can create responsive and dynamic applications that interact smoothly with external systems. If you have any questions or need more examples, feel free to ask!