Side-Effect & useEffect in React

Gurinderpal Singh Narang
4 min readFeb 23, 2024

--

What are side effects?

In React, “side effects” refer to any operations or behaviors that occur in a component after rendering, and that don’t directly impact the current component render cycle. These side effects can include tasks such as data fetching, subscriptions, manually changing the DOM, or other interactions with the outside world. One common use case for handling a side effect is making an asynchronous data request, such as fetching data from an API.

Fetching data from an API is a standard asynchronous operation in a web application. Let’s explore how to achieve this in a React project. When you begin writing code in React to fetch data from an API, you may encounter an issue where your application enters into an infinite rendering loop. This occurs because updating the state triggers a re-render, leading to the execution of the same code again.

import React, { useState, useEffect } from "react";

function App() {
const [studentList, setStudentList] = useState([]);
const api = "https://hp-api.onrender.com/api/characters/house/gryffindor";
//Fetching data from the API
fetch(api)
.then((response) => response.json())
.then((data) => {
setStudentList(data);
})
.catch((error) => {
console.log("Error while fetcing data: ", error);
});
return (
<>
<h1>List of students in Howarts</h1>
{studentList.map((student) => (
<>
<h4>{student.name}</h4>
</>
))}
</>
);
}

The above code will lead to an infinite rendering of our application. So what I am trying to achieve here is, I tried to fetching data from an API and once I will the get the data I am updating the state and once a state is updated in the a component a re-render will happen. When the component will re-render same code will execute again which will lead my application to infinite re-render. Now let’s discuss how to handle these kind of scenarios in React.

Addressing Infinite Rendering with useEffect()

To handle scenarios like the one described above, React provides a powerful hook called useEffect. useEffect is a hook in React that allows you to perform side effects in function components. useEffect function takes two arguments: a function containing the code for the side effect, and an optional array of dependencies. Let’s break down both the arguments:

  1. Effect function: The first argument of useEffect is a function containing the code for the side effect. This function, commonly referred to as the "effect function," is executed after the component renders for the first time and after every subsequent render.
  2. Dependency array: The second argument of useEffect is an optional array of dependencies. It specifies the values (variables or state) that the effect function depends on. When any of the dependencies change between renders, the effect function will be re-executed.

The Fix

Here’s how to fix the infinite rendering issue using useEffect:

import React, { useState, useEffect } from "react";

function App() {
const [studentList, setStudentList] = useState([]);
const api = "https://hp-api.onrender.com/api/characters/house/gryffindor";
//Getting data from the
useEffect(() => {
fetch(api)
.then((response) => response.json())
.then((data) => {
setStudentList(data);
})
.catch((error) => {
console.log("Error while fetcing data: ", error);
});
}, []);
return (
<>
<h1>List of students in Howarts</h1>
{studentList.map((student) => (
<div key={student.id}>
<h4>{student.name}</h4>
</div>
))}
</>
);
}

export default App;

In this corrected code, the useEffect hook ensures that the data fetching operation only occurs once, when the component mounts, preventing the infinite rendering loop (due to the empty dependency array []). The studentList state is updated after the asynchronous data request is complete, and the component renders correctly.

Let’s discuss useEffect in some more detail along with difference phases of a functional component. Here are the different phases of a functional component and how useEffect can be used to achieve various tasks during these phases:

Mounting Phase:

Initialisation: useEffect with an empty dependency array runs once after the initial render. It simulates componentDidMount in class components.

useEffect(() => {
// Code to run after the initial render
}, []);

Updating Phase:

Dependencies Change: If any dependencies specified in the dependency array change, the useEffect callback is re-executed. It is similar to componentDidUpdate in class components.

const [value, setValue] = useState('');
useEffect(() => {
// Code to run when value changes
}, [value]);

Without Dependencies: If you don’t provide a dependency array, the useEffect callback runs after every render. It is similar to componentDidUpdate without specific dependencies.

useEffect(() => {
// Code to run after every render
});

Unmounting Phase:

Cleanup: useEffect can be used to return a cleanup function, simulating componentWillUnmount in class components.

useEffect(() => {
// Code to run on component mount

return () => {
// Code to run on component unmount
};
}, []);

Note: useEffect without dependencies can lead to unintentional side effects and reduced performance, so it's generally a good practice to specify dependencies when possible. Also, always clean up resources in the cleanup function to prevent memory leaks.

In summary, useEffect provides better control over the component's lifecycle and ensures a more predictable behavior when dealing with asynchronous operations like data fetching.

Thanks for Reading!!!

--

--

Responses (1)