One of the most difficult aspects of learning React is understanding how components interact with the browser throughout their lifecycle. We have lifecycle methods in class components like componentDidMount, componentDidUpdate, and componentWillUnmount to handle side effects like data fetching or DOM changing. But how can we address this in functional components? Here's where the useEffect hook comes in!
In this post, we'll go over how useEffect works and how it helps us manage the component lifecycle in functional components.
What is a Component Lifecycle?
Every React component goes through three stages: mounting, updating, and unmounting.
- Mounting: The component is rendered for the first time.
- Updating: When the component's props or state change, it re-renders.
- Unmounting: The component is removed from the DOM.
Lifecycle methods in class components include componentDidMount, which performs actions after the component has been added to the DOM, and componentWillUnmount, which cleans up resources after the component is removed. Functional components lack these methods; however, React provides the useEffect hook to handle all of this!
Introducing the useEffect Hook
The useEffect hook enables you to implement side effects in your functional components. Side effects are activities that occur outside of the component's render logic, such as obtaining data from an API, dealing directly with the DOM, or setting timers.
Implementation
import React, { useState, useEffect } from 'react';
const ExampleComponent = () => {
const [count, setCount] = useState(0);
useEffect(() => {
// This effect runs after every render
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
};In this example, every time the component renders, the useEffect hook updates the document title to reflect the latest count.
Understanding Lifecycle Methods with useEffect
While useEffect is powerful, it can be tricky to understand how it maps to lifecycle methods. Let’s break it down:
When the component first renders
To run an effect only when the component mounts (and not on subsequent updates), you can pass an empty array [] as a second argument to useEffect.
useEffect(() => {
console.log('Component mounted');
}, []);When the component updates
By default, useEffect runs after every render (mount and update). If you want to control when it runs, you can pass a dependency array that contains the variables you want to watch. useEffect will only run when those variables change.
useEffect(() => {
console.log('Count changed:', count);
}, [count]);When the component unmounts
To perform cleanup (e.g., removing event listeners or canceling API requests), return a function inside useEffect. This function will run when the component unmounts, similar to componentWillUnmount.
useEffect(() => {
console.log('Component mounted');
// Cleanup function
return () => {
console.log('Component will unmount');
};
}, []);
Controlling When useEffect Runs
UseEffect is sometimes set to execute only when specific variables change. The dependence array is used in this situation.
- If you pass an empty array ([]), the effect runs only once, after the initial render.
- If you pass a variable (e.g., [count]), the effect will run only when count changes.
Here is an example:
useEffect(() => {
console.log('Count or name changed');
}, [count, name]); // Effect will run only when `count` or `name` changes
Conclusion
The useEffect hook is an effective way to manage side effects in functional components. You can manage component lifecycle events, schedule the execution of your effects, and maintain the cleanliness of your components by correctly cleaning them as needed by knowing how to use it.