ReactJS | useEffect Hook
The motivation behind the introduction of useEffect Hook is to eliminate the side-effects of using class-based components. For example, tasks like updating the DOM, fetching data from API end-points, setting up subscriptions or timers, etc can lead to unwarranted side-effects. Since the render method is too quick to produce a side-effect, one needs to use life cycle methods to observe the side effects. For example, consider updating the document title for a simple counter component to the current value. On the initial render, we set the current clicked value to 0 clicks. So, this section is coded into the componentDidMount() method which is executed only once in the component life cycle. Then we create a button to increment the count state value by one on every click. As the count value state changes, we also need to update the document title again and for that, we need to write the same piece of code in componentDidUpdate(). The componentDidupdate() method is perfect for updating the counter value at any time the state changes but the repetition of code is one of the side-effects.
Javascript
componentDidMount(){ document.title = `you clicked ${ this .state.count} times`; } componentDidUpdate(){ document.title = `you clicked ${ this .state.count} times`; } |
Let us consider another side-effect by setting up a timer. On the componentDidMount() method, we set a timer to log a string “hello” every 5 seconds. We can clear this timer when the component is being removed from the DOM. And we do that in componentWillUnmount() life-cycle method. So the code for the timer looks like below:
Javascript
componentDidMount(){ this .interval = setInterval( this .tick, 1000) } componentWillUnmount(){ clearInterval( this .interval) } |
Both the counter and timer when merged to form a single component looks like below:
Javascript
componentDidMount(){ document.title = `you clicked ${ this .state.count} times`; this .interval = setInterval( this .tick, 1000) } componentDidUpdate(){ document.title = `you clicked ${ this .state.count} times`; clearInterval( this .interval) } |
As you observe the code above, we tend to notice that to update the document title we write the same code twice, once in componentDidmount() and once in componentDidUpdate(). The second thing to observe is how the code is split into component. The code related to the timer, setInterval, and clearInterval which are related are put into different code blocks (i.e. different life-cycle methods). The code to update the DOM and code for setting up the timer which is completely unrelated are put in the same life-cycle method (i.e. in componentDidMount()). It will be much better if there is an option to not repeat code at the same time and group together related codes in the same block. This is where the useEffect Hook comes in the picture.
The Effect Hook lets you perform side effects in functional components. It is a close replacement for the componentDidMount(), componentDidUpdate(), and componentWillUnmount() methods.
useEffect after render: We know that the useEffect() is used for causing side effects in functional components and it is also capable of handling componentDidMount(), componentDidUpdate(), and componentWillUnmount() life-cycle methods of class-based components into the functional components. Let’s look at an example of how to use the useEffect hook as a feature that can mimic the above-mentioned life-cycle methods but in functional components.
For better understanding let’s look at how the code looks like in a class component below and we are going to name it “ClassComponentOne”
Filename: src/components/ClassCounterOne.js
Javascript
import React, { Component } from 'react' class ClassCounterOne extends Component { constructor(props){ super (props) this .state = { count: 0 } } componentDidMount(){ document.title = `Clicked ${ this .state.count} times` } componentDidUpdate(prevProps, prevState){ document.title = `Clicked ${ this .state.count} times` } render() { return ( <div> <button onClick = {() => this .setState( { count: this .state.count + 1})}> Click { this .state.count} times </button> </div> ) } export default ClassCounterOne |
Now, we include the component in App.js. The code will look like below for App.js
Filename: src/App.js
Javascript
import React from 'react' import './App.css' import ClassCounterOne from './components/ClassCounterOne' function App(){ return ( <div className= 'App' > <ClassCounterOne /> </div> ) } export default App |
Now if we look at the browser we can observe that initially the document title is “Clicked 0 times”.
And if we click on the button the count value increments by 1 on each click and updates the title as well.
Now let’s try to replace the functionality with a functional component. For the same purpose create a new file and name it (say, HookCounterOne.js)
The functional component will look like the code below:
Filename: src/components/HookCounterOne.js
Javascript
import { useState, useEffect } from 'react' function HookCounterOne() { const [count, setCount] = useState(0) useEffect(() => { document.title = `You clicked ${count} times` },[count]) return ( <div> <button onClick = {() => setCount((prevCount) => prevCount + 1)}> Click {count} times </button> </div> ) } export default HookCounterOne |
Now we need to import the above component in our App.js file. After the inclusion of the HookCounterOne component, the code looks like below:
Filename: src/App.js
Javascript
import React from 'react' import './App.css' import HookCounterOne from './components/HookCounterOne' function App(){ return ( <div className= 'App' > <HooKCounterOne /> </div> ) } export default App |
Now if we look into the browser we can see the initial state as below. Initially, the document title reads “You clicked 0 times”.
And when you click on the button, the count value increments, and the document title is updated. As we can observe the behavior is as expected.
When we specify useEffect we are basically requesting react to execute the function that we pass in the useEffect function as an argument, everytime the component renders. The second thing to make note of is that useEffect is used inside the component as by doing this we can easily access the components state and props without having to write any additional code.
If you want to use the react lifecycle methods in functional components then copy paste the code snippet given below and customize them as per your requirement.
- For componentDidMount
Javascript
useEffect(()=>{ //You can add your code here for mounting phase of component console.log( "Mounting in Functional Component" ) },[]) // adding an empty array ensures that the useEffect is only triggered once // (when the component mounts) |
- For componentDidUpdate
Javascript
useEffect(()=>{ //You can add your code for updating phase of component console.log( "Updating in Functional Component" ) },[values]) //values triggers re render whenever they are updated in your program, //you can add multiple values by separating them by commas |
- For componentWillUnmount
Javascript
useEffect(()=>{ return ()=>{ //You can add your code for unmounting phase of component console.log( "Functional Component Removed " ) } },[]) //Write all the code of unmounting phase only inside the callback function |
Please Login to comment...