ReactJS - Using useEffect



React provides useEffect to do side-effects in a component. Some of the side effects are as follows −

  • Fetching data from external source & updating the rendered content.

  • Updating DOM elements after rendering.

  • Subscriptions

  • Using Timers

  • Logging

In class based components, these side effects are done using life cycle components. So, useEffect hook is an effect replacement for below mentioned life cycle events.

  • componentDidMount − Fires after the rendering is done for the first time.

  • componentDidUpdate − Fires after the rendering is updated due to prop or state changes.

  • componentWillUnmount − Fires after the rendered content is unmounted during destruction of component.

Let us learn how to use effect hook in this chapter.

Signature of useEffect

The signature of useEffect is as follows −

useEffect( <update function>, <dependency> )

where, the signature of the update function is as follows −

{
   // code
   return <clean up function>
}

Here,

Update function − Update function is the function to be executed after each render phase. This corresponds to componentDidMount and componentDidUpdate events

Dependency − Dependency is an array with all the variables on which the function is dependent. Specifying the dependency is very important to optimize the effect hook. In general, update function is called after each render. Sometimes it is not necessary to render update function on each render. Let us consider that we are fetching data from external source and updating it after the render phase as shown below −

const [data, setDate] = useState({})
const [toggle, setToggle] = useState(false)
const [id, setID] = useState(0)
useEffect( () => {
   fetch('/data/url/', {id: id}).then( fetchedData => setData(fetchedData) )
})
// code

Component will rerender whenever data and toggle variable are updated. But as you see, we don't need that the defined effect to be run during each update of toggle state. To fix the issue, we can pass an empty dependency as shown below −

const [data, setDate] = useState({})
const [toggle, setToggle] = useState(false)
const [id, setID] = useState(0)
useEffect( () => {
   fetch('/data/url/', { id: id }).then( fetchedData => setData(fetchedData) )
}, [])

The above code will run the effect only once after the first render. Even though it will fix the issus, the effects has to be run on every change of id. To make it happen, we can include id as dependency for the effects as shown below −

const [data, setDate] = useState({})
const [toggle, setToggle] = useState(false)
const [id, setID] = useState(0)
useEffect( () => {
   fetch('/data/url/', { id: id }).then( fetchedData => setData(fetchedData) )
}, [id])

This will ensure that the effects will rerun only after the modification of id

Cleanup function − Cleanup function is used to cleanup work during the usage of subscription function and timer function as shown below −

const [time, setTime] = useState(new Date())
useEffect(() => {
   let interval = setInterval(() => {
      setTime(new Date())
   }, 1000)
   return () => clearInterval(interval)
}, [time])

Let us create a complete application to understand the cleanup function in later section.

Features of effect hook

Some of the notable features of effect hook are as follows −

  • React allows multiple effect hook to be used in a function component. This will help us to write a function for each side effects and set it up as separate effect.

  • Each hook will be run in the order in which it is declared. Developer should make sure that the order of effects are declared correctly.

  • Dependency feature can be used to improve the performance and correct working of the side effects.

  • Cleanup function prevents memory leaks and unnecessary firing of events.

Fetching data using effect

Let us create an application that will fetch data from external source and render it using useEffect hook in this section.

First of all, create a new react application and start it using below command.

create-react-app myapp
cd myapp
npm start

Next, create a react component, NameList under component folder (src/components/NameList.js)

function NameList() {
   return <div>names</div>
}
export default NameList

Here, the purpose of the NameList component is to showcase the popular list of common names

Next, update the root component, App.js to use the newly created NameList component.

import NameList from "./components/NameList";
function App() {
   return (
      <div style={{ padding: "5px"}}>
         <NameList />
      </div>
   );
}
export default App;

Next, create a json file, names.json (public/json/names.json) and store popular names in json format as shown below.

[
   {
      "id": 1,
      "name": "Liam"
   },
   {
      "id": 2,
      "name": "Olivia"
   },
   {
      "id": 3,
      "name": "Noah"
   },
   {
      "id": 4,
      "name": "Emma"
   },
   {
      "id": 5,
      "name": "Oliver"
   },
   {
      "id": 6,
      "name": "Charlotte"
   },
   {
      "id": 7,
      "name": "Elijah"
   },
   {
      "id": 8,
      "name": "Amelia"
   },
   {
      "id": 9,
      "name": "James"
   },
   {
      "id": 10,
      "name": "Ava"
   },
   {
      "id": 11,
      "name": "William"
   },
   {
      "id": 12,
      "name": "Sophia"
   },
   {
      "id": 13,
      "name": "Benjamin"
   },
   {
      "id": 14,
      "name": "Isabella"
   },
   {
      "id": 15,
      "name": "Lucas"
   },
   {
      "id": 16,
      "name": "Mia"
   },
   {
      "id": 17,
      "name": "Henry"
   },
   {
      "id": 18,
      "name": "Evelyn"
   },
   {
      "id": 19,
      "name": "Theodore"
   },
   {
      "id": 20,
      "name": "Harper"
   }
]

Next, create a new state variable, data to store popular names in NameList component as shown below −

const [data, setData] = useState([])

Next, create a new state variable, isLoading to store loading status as shown below −

const [isLoading, setLoading] = useState([])

Next, use fetch method to get popular names from json file and set it into data state variable inside useEffect hook

useEffect(() => {
   setTimeout(() => {
      fetch("json/names.json")
         .then( (response) => response.json())
         .then( (json) => { console.log(json); setLoading(false); setData(json); } )
   }, 2000)
})

Here we have,

  • Used setTimout method to simulate the loading process.

  • Used fetch method to get the json file.

  • Used json method to parse the json file.

  • Used setData to set the names parsed from json file into data state variable.

  • Used setLoading to set the loading status.

Next, render the names using map method. During fetching, show loading status.

<div>
   {isLoading && <span>loading...</span>}
   {!isLoading && data && <span>Popular names: </span>}
   {!isLoading && data && data.map((item) =>
      <span key={item.id}>{item.name} </span>
   )}
</div>

Here we have,

  • Used isLoading to show the loading status

  • Used data variable to show the list of popular names

The complete source code of the component, NameList is as follows −

import { useState, useEffect } from "react"
function NameList() {
   const [data, setData] = useState([])
   const [isLoading, setLoading] = useState([])
   useEffect(() => {
      setTimeout(() => {
         fetch("json/names.json")
         .then( (response) => response.json())
         .then( (json) => { console.log(json); setLoading(false); setData(json); } )
      }, 2000)
   })
   return (
      <div>
         {isLoading && <span>loading...</span>}
         {!isLoading && data && <span>Popular names: </span>}
         {!isLoading && data && data.map((item) =>
            <span key={item.id}>{item.name} </span>
         )}
      </div>
   )
}
export default NameList

Next, open the browser and check the application. It will show the loading state and after 2 second, it will fetch the json and showcase the popular names as shown below −

Fetching Data using Effect

Fetching Data using Effect

DOM mutations

The useEffect hook can be used to manipulate the document using DOM and its methods. It makes sure that the code inside it will execute only after DOM is ready. Let us change our name list application and update the title of the page using DOM mutation.

First of all, open NameList component and add the document title based on the loading status as shown below −

useEffect(() => {
   if(isLoading)
      document.title = "Loading popular names..."
   else
      document.title = "Popular name list"
   setTimeout(() => {
      fetch("json/names.json")
         .then( (response) => response.json())
         .then( (json) => { console.log(json); setLoading(false); setData(json);} )
   }, 2000)
})

Here, we have used the DOM object, document.title to update the title to the page.

Finally, open the browser and check how the title of the document is updated through DOM manipulation

DOM Mutations

DOM Mutations

Cleanup function

useEffect can be used to remove the cleanup functions such as clearInterval, removeEventListener etc., during the unmounting of the component from the page document. This will prevent the memory leaks and improve the performance. To do this, we can create our own cleanup function and return it from the useEffect callback argument.

Let us change our name list application to use setInterval instead of setTimeout and later use clearInterval to remove set callback function during unmounting the component.

First of all, open NameList component and update the useEffect section as shown below −

useEffect(() => {
   if(isLoading)
      document.title = "Loading popular names..."
   else
      document.title = "Popular name list"
   let interval = setInterval(() => {
      setLoading(true)
      fetch("json/names.json")
         .then( (response) => response.json())
         .then( (json) => { console.log(json); setLoading(false); setData(json);} )
      }, 5000)
   return () => { clearInterval(interval) }
})

Here we have,

  • Used setImterval to update the popular names every 5 seconds.

  • Used clearInterval in the clean up function to remove the setInterval during unmounting of the component.

Finally, open the browser and check how the application behaves. We will see that the data get updated every 5 seconds. When the component is unmounted, clearInterval will be called in the background.

Summary

useEffect is an essential feature of a function component and enables the components to use life cycle events. It helps the function component to provide rich functionality with predictable and optimized performance.

Advertisements