Before the release of Hooks, React components were divided into 2 broad categories:
- Functional components → Stateless components; unable to access lifecycle methods
- Class components → Stateful components; able to define states and access lifecycle methods
This meant that the only way to access lifecycle methods was to write the component as a Class. With the introduction of React hooks, it is now possible to create stateful components without the use of a Class.
First, a quick recap of React lifecycles:
Mounting
This is the first lifecycle of a React component, the stage where it is created and inserted into the DOM. In class components, we have the componentDidMount
method, which allows us to use setState
to modify our states after the first render.
componentDidMount() {
console.log("The component has mounted successfully!");
this.setState({isLoading: false});
}
Updating
This lifecycle happens after a component is mounted and rendered into the DOM. The component is updated when we have an update in our props or state. The componentDidUpdate
method is invoked after the update happens in the component and is used to compare whether a specified prop or state has changed.
componentDidUpdate(prevProps) {
if (this.props.name !== prevProps.name) {
console.log("Name has changed!");
}
}
Unmounting
This lifecycle is responsible to do the cleanup in our DOM i.e. when we want to remove the component. The componentWillUnmount
method is invoked when the component is about to be removed from the DOM:
componentWillUnmount() {
console.log("Component will be unmounted!");
}
useEffect vs. Lifecycle methods
In class components we have lifecycle methods to perform actions in a specific lifecycle stage of a component. For functional components, we can use the useEffect
hook to achieve the same outcomes. The hook has the following signature:
import { useEffect } from 'react';
useEffect(() => {
// inside this callback function we perform our side effects
});
It receives a callback function as the first argument, which, just like lifecycle methods, will be called after every render.
The second argument (optional) is an array of dependencies that the hook is going to watch i.e. the hook will only run if one of those dependencies change.
This means that when you pass an empty array to the useEffect
hook, it will only run once after render.
-
Mounting - Just by using the
useEffect
hook, we are performing the equivalent of thecomponentDidMount
method:
useEffect(() => {
console.log("The component has mounted successfully!");
setisLoading(false);
}, []);
-
Updating - By passing an array of dependencies that we want to watch, the hook will update when any of the dependencies change. For example, if you have a prop name
count
that is passed into theuseEffect
hook, the hook will only run whencount
changes.
useEffect(() => {
console.log("You have clicked ${count} times")
}, [count]);
-
Unmounting - We return a function within the callback function of the hook, which will achieve the same outcome as a
componentWillUnmount
method.
useEffect(() => {
console.log("You have clicked ${count} times")
return () => console.log("Component will be unmounted!");
}, [count]);
To recap:
Up until React 16.8, functional components were stateless and could not access lifecycle methods. With the introduction of hooks, we are now able to add state and perform side effects to our functional components.
The
useEffect
hook is a function that allows us to perform side effects in our functional components. It receives a callback function as its first argument → we define our effects here.The second argument (optional) is an array of dependencies that the hook is going to watch. If you pass an empty array, the hook will not be watching any dependencies and therefore will only run once after the initial render.
If you don't pass a second argument into the hook, the effect will run after every render 🤪
Thoughts? Feedback? Let me know in the comments below 👇🏻
Top comments (0)