All you need to know about React’s useEffect Hooks

Mayank Gupta
Sep 4 · 8 min read

Hooks are the new features of React 16.8. We can create state variables and other features of React without using Class-Based Components. Hooks are used for Function Components. We have various Hooks provided by React:

  • useState Hook
  • useEffect Hook
  • useContext Hook
  • useRef and many others…

This piece is focused on useEffect Hooks. We will be talking about the advantages and uses of this Hook in detail.

For an introduction to React Hooks, refer to this article…


Understanding Side Effects

React is centered around functional programming. A side effect is any execution that affects something outside the scope of the function being executed. It is not a React specific term, it’s a general concept about the behavior of a function. For example, if a function modifies a global variable, then that function is introducing a side effect — the global variable doesn’t belong to the scope of the current function.

Some examples of side effects in React components are:

  • Making asynchronous API calls for data
  • Setting a subscription to an observable
  • Manually updating the DOM element
  • Updating global variables from inside a function

Side effects are often unavoidable if we want to make components perform the required task. Say we require a component that fetches data for the current user. In order to create this, we need to make an AJAX call to fetch the data for the specified user, hence we’re introducing the side effect to the function. Even if they are called side effects, you need them inside your components for them to accomplish the required tasks.


Introduction to Effect Hooks

Hooks are available for functional components. Let’s introduce a simple useEffect to our functional component:

import React, { useEffect } from "react";

function SimpleUseEffect() {

useEffect(() => {
alert("Component Rendered")
});

return (
<div>
<b>A Simple use of useEffect...</b>
</div>
)
}

In the code above, we used the useEffect hook. It takes a function as input, which is executed after the initial rendering, as well as re-rendering, of the component. After each rendering one the DOM has been updated and the function passed to useEffect is invoked. In the above scenario, the component give an alert after the initial rendering of the component.

Let’s introduce a side effect to the functional component using useEffect:

import React, { useEffect } from "react";

function SimpleUseEffect() {

useEffect(() => {
document.getElementById("userName").value = "Mayank";
});

return (
<div>
<b>A Simple use of useEffect...</b>
</div>
)
}

This code introduces a side effect to the function component by updating a DOM element. Since the DOM element does not belong to this function, updating the DOM inside the useEffect hook introduces a side effect to the component.


Using Multiple useEffect to Separate Concerns

React enables multiple useEffect instances inside a React functional component. The code can be broken down into multiple Hooks containing logically related code in a single function. Let’s look at the code below to understand the scenario:

import React, { useEffect } from "react";
import axios from 'axios';

function SimpleUseEffect() {

useEffect(() => {
document.getElementById("userName").value = "Mayank";
});

useEffect(() => {
axios.get(`https://jsonplaceholder.typicode.com/users`)
.then(res => {
console.dir(res.data);
})
});

return (
<div>
<b>A Simple use of useEffect...</b>
</div>
)
}

In the above code, we have multiple side effects that need to be introduced to the function. In one of the useEffect functions, we need to update the DOM element and in other, we need to make an AJAX call and log the data to the user. Both of the tasks are logically independent, so they can be segregated into multiple “useEffect” hooks so that they can accomplish the task inside separate logical blocks. Logical segregation into multiple functions.


Re-Rendering React Functional Component

In the code below, whenever the user clicks the button, the state variable, userCount will be incremented by one. Using the useState hook, we’re creating a state variable in the functional component. As soon as the state variable updates, the react component will be re-rendered. After each re-rendering, the “useEffect” function will be re-invoked.

In the above case, as soon as the count of employee (userCount variable) is incremented, the relevant DOM element is updated and we get an alert notifying us that the component has been updated, since that code is associated with the effect added to the component.

import React, { useEffect } from "react";
import axios from 'axios';

function SimpleUseEffect() {

let [userCount, setUserCount] = useState(0);

useEffect(() => {
alert("Component Updated");
});

return (
<div>
<b>User Count: {userCount}</b>
<input type="button" onClick={() => setUserCount(userCount + 1}}>Add Employee</input>
</div>
)
}

If we have multiple instances of useEffect in the component, all the useEffect functions will be executed in the same order as they are defined inside the component.


Let’s Summarize

What does useEffect do?

Using this Hook, we tell the component to execute some logic after rendering the component. React calls for the function passed to the “useEffect” initial rendering and re-rendering of the component

Why is useEffect defined inside a component?

“useEffect” function is defined inside the component so that the variables and the functions defined inside the components can be accessed directly. It takes advantage of the concept of Closure to provide access to the local functions and variables defined inside a function.

Does useEffect execute after every Rendering?

By default, it runs during initial rendering as well as during the re-rendering of the component. React runs the hook associated with the component after each call to render.


Optimizing Performance By Skipping “useEffect”

There might be multiple state variables that exist inside the component. Change to any state variable leads to the re-rendering of the component. On each re-rendering, the useEffect hooks are invoked by default.We may need to run useEffect in a specific scenario, rather than executing it for each state change.

The application can have multiple instances of useEffect, and we can configure them to execute only if a specific state or props value is updated. We can skip the execution of useEffect on re-rendering on the basis of state or props value updates. Let’s see this with this simple example:

import React, { useEffect } from "react";
import axios from 'axios';

function SimpleUseEffect() {

let [userCount, setUserCount] = useState(0);
let [simpleCount, setSimpleCount] = useState(0);

useEffect(() => {
alert("Component User Count Updated...");
}, [userCount]);

useEffect(() => {
alert("Component Simple Count Updated");
}, [simpleCount]);

return (
<div>
<b>User Count: {userCount}</b>
<b>Simple Count: {simpleCount}</b>
<input type="button" onClick={() => setUserCount(userCount + 1}} value="Add Employee" />
<input type="button" onClick={() => setSimpleCount(simpleCount + 1}} value="Update Simple Count" />
</div>
)
}

In the code above, we have two useEffect Hooks. Each of the hooks takes two parameters as input, the first parameter defines the function that needs to be invoked and the second parameter represents the list of state or the props variables which will trigger the function if updated.

The first useEffect will be invoked only if the value of userCount updates Similarly the second useEffect will trigger only if the value of simpleCount has been updated.

So we are restricting the execution of the useEffect function on the basis of the update to specific properties available to the component, either as props or state variable. We will receive a “Component User Count Updated…” alert message only when the value of userCount is updated, otherwise the effect is not executed during the re-rendering of the component.


Only Running useEffect Once

There could be a scenario in which the developer does not want to run the effect again, even if the state or props value updates. In this scenario, we can pass an empty array as the second parameter to the useEffect function.

import React, { useEffect } from "react";
import axios from 'axios';

function SimpleUseEffect() {

let [userCount, setUserCount] = useState(0);

useEffect(() => {
alert("Component Simple Count Updated");
}, []);

return (
<div>
<b>User Count: {userCount}</b>
<input type="button" onClick={() => setUserCount(userCount + 1}} value="Add Employee" />
</div>
)
}

In the above code we’re passing an empty array to the useEffect function. Passing an empty array notifies React that the effect is not dependent on any update to props or state variable. Since it is not dependent on state or props, the component does not re-run the effect on each re-rendering. It is executed only once during initial rendering.


Cleanup in useEffect

Returning a function from effect is an optional cleanup mechanism. Each effect may or may not return a function that would execute cleanup code.

Effect may contain certain code that requires cleanup during component unmounting phase or during the next render cycle to avoid memory leaks. We can create a cleanup code for each effect that we have inside a component. The cleanup code will be executed when the component unmounts or during next rendering cycle. By default, the effects are executed for each rendering.

import React, { useEffect } from "react";
import axios from 'axios';

function SimpleUseEffect() {

let [userCount, setUserCount] = useState(0);

useEffect(() => {
someRandomObservable.subscribe((data) => {
console.dir(data);
});
});

useEffect(() => {
document.getElementById("userName").value = "Mayank";
});

return (
<div>
<b>User Count: {userCount}</b>
<input type="button" onClick={() => setUserCount(userCount + 1}} value="Add Employee" />
</div>
)
}

The code above contains two useEffect hooks inside the component. One of the effects is used to subscribe to some observable data whereas the other effect is used to just update the DOM element. When the component is rendered for the first time, it executes the effect in the specified scenario. As a part of the first effect, the component subscribes to the observable and while executing the second effect, it updates the DOM element manually.

When the component has to be unmounted or during the next render cycle, we should unsubscribe to the observable that we have subscribed to, in order to avoid memory leaks.

In the example specified above, if the observable is not unsubscribed, calling the effect again will lead to subscribing the observable multiple times. For this reason we need to unsubscribe before the next render cycle. The code for the cleanup can be specified inside an effect as a return function. Each effect can have a separate implementation of the cleanup code, depending on the work that is execution that is made inside the effect:

import React, { useEffect } from "react";
import axios from 'axios';

function SimpleUseEffect() {

let [userCount, setUserCount] = useState(0);

useEffect(() => {
someRandomObservable.subscribe((data) => {
console.dir(data);
});

return () => {
someRandomObservable.unsubscribe();
};
});

return (
<div>
<b>User Count: {userCount}</b>
<input type="button" onClick={() => setUserCount(userCount + 1}} value="Add Employee" />
</div>
)
}

To specify the code for the cleanup, we add an anonymous return function to the component. The return function is executed every time when the component re-renders. Effect runs for each render, for that reason, React performs cleanup from the previous render before running the effect next time. The code above unsubscribes to the observable on each render and then subscribes to the observable again while running the effect again.


Conclusion

useEffect can be seen as a substitution to the lifecycle events of React class based components. We can use this effect to replicate events like componentDidUpdate, componentDidMount, componentWillUnmount and shouldComponentUpdate.

Try updating to React Hooks some time — it’s an interesting feature and it might make you think more in terms of functional programming.

Better Programming

Advice for programmers.

Mayank Gupta

Written by

9 Years of Experience with Front-end Technologies and MEAN Stack. Working on all Major UI Frameworks like React, Angular and Vue.

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade