useEffect() — what, when and how

Abhishek Ranjan
6 min readSep 12, 2021

--

useEffect() is a react hook which you will use most besides useState(). You’ll often use this hook whenever you need to run some side effects (like sending http requests) in your component. So, there comes a lot of questions: what are these side effects, when to use the useEffect() hook and how to write the syntax.

Photo by Ferenc Almasi on Unsplash

What are the side effects in a react component?

The job of react is to render and re-render UI whenever needed (after changing state, props). Other than this like sending http requests to the backend server, updating global variables inside the component, saving or updating caches etc. are considered as side effects. We should not use side effects inside the main component as it may create bugs like sending too many http requests.

import React, { useState } from "react";
export const Test = () => {
const [value, setValue] = useState(false);setTimeout(() => {
console.log("request sent");
}, 2000);
const clickHandler = () => {
setValue((prevState) => !prevState);
};
return (
<div>
<h1>{value ? "hello" : "hey"}</h1>
<button onClick={clickHandler}>Click me</button>
</div>
)};

Here, I have used setTimeout. Whenever the button is clicked the state of value will be changed, the Test component is re-rendered hence the setTimeout() function will run again and again. So, whenever any state or prop is changed in the component, request will be sent.

Instead of calling setValue in clickHandler if we call setValue in setTimeout funtion then it will cause infinite loop.

setTimeout(() => {
console.log(“request sent”);
setValue((prevState) => !prevState);
}, 2000);

To avoid such bugs in our code we use the useEffect hook. Let’s take a deep dive into the hook and learn how to use it.

useEffect React Hook

Syntax :- useEffect( ()=>{} , [dependencies] ). It takes two arguments separated with a comma, first — a function that you want to execute whenever useEffect runs. In the above case we shall run setTimeout inside useEffect. Second — an array of dependencies.

useEffect runs after the rendering/re-rendering of the component but only if any of the dependencies is changed. Remember it runs after the component is rendered(or mounted) not before, not along with but only after the component is rendered. Let me show you this with an example.

import React, { useEffect } from "react";export const Test = () => {useEffect(() => {
console.log("Effect");
}, []);
console.log(“rendered”);return <>{console.log("UI rendered")}</>;

The console output will be in the order : rendered , UI rendered , Effect

The 2nd argument (array of dependencies)

We will see three cases to understand use of dependencies in useEffect hook

Case I. Array with few dependencies :-

Here the useEffect has two dependencies — value1 and value2.

The “effect” will log in the console —

  1. For the very first time when the component is rendered (because for the very first time value of dependencies value1 and value2 seem to be changed to react).
  2. Again if any of the states of value1 or value2 will be changed but not if the state of value3 is changed.

CASE II. Array with no dependencies (empty array).

Here useEffect has the 2nd argument of empty array.

The “effect” will be logged only when the component is rendered very first time. It will not run if “the state of value” is changed (the component will re-render but still the useEffect won’t run).

Now, the question comes why did the effect run only once. Here is answer to your question —

For the very first time useEffect runs after component evaluation because you had no dependencies before. But once it ran for the very first time, now it has no dependencies but also they didn’t change compared to the first execution cycle.

Case III. Without the 2nd argument [ useEffect( ()=> {} ) ]

Can we have useEffect without the 2nd argument ? Yes, and here is our third and last case. You will use this case very rarely. In this case useEffect runs after every render and re — render of the component.

The “effect” text will be logged every time after component renders or re-renders. Here comes one more interesting question — what will be the difference if instead of

useEffect(() => {
console.log(“effect”);
});

We directly write —

console.log(“effect”);

The difference is that the first one will run ( “effect” will be logged ) after the component is evaluated or re — evaluated while the second one will run (“effect” will be logged) during / along with the component evaluation/re-evaluation.

In a nutshell

Case I. useEffect with dependency array having some values. useEffect(()=>{}, [ el1, el2, el3 ])

i) It will run once after the component renders for the very first time.

ii) Again it will run after the component re-renders but only if the value of any of the dependencies changes. If the component re-renders but the value of any of the dependencies do not change then useEffect won’t run.(The component may re-render because of a change in state or props.)

Case II. useEffect with empty dependency array. useEffect(()=>{},[ ])

It will run only once — after the component renders for the very first time.

Case III. useEffect with no dependency array. useEffect(()=>{})

It is a valid syntax. It will run after the component renders and after every re-render of the component.

There are no different cases for react to handle useEffect.I made up these 3 cases only for the understanding purpose. Once you understand how useEffect works you will know that these cases are not actually different, they work on the same principle.

What elements should you use in the dependency array?

Key rule to add elements in the dependency array is : “Add all the variables/functions/states/props in your effect function which could change when your component or some parent component is re-rendered”

What you should not add as dependency —

  • No need to add state updating functions such as setValue. setValue function is never going to change even if the component re-renders.
  • No need to add built in functions like fetch as these will also never change.
  • *Very Important* Never add the states as dependencies which are being changed in the effect function. Otherwise it will cause an infinite loop.
const [value, setValue] = useState(false);
useEffect(() => {
console.log(“effect”);
setValue((prevState) => !prevState);
},[value]);

The above code is going to create an infinite loop — for the very first time the component will render and then useEffect will run which will change the state of value so the component will re-render again. Again the useEffect will run as value is a dependency and the state of value will be changed so the component will re-render again resulting in an infinite loop.

const [value, setValue] = useState(false);
useEffect(() => {
console.log(“effect”);
setValue((prevState) => !prevState);
}, []);

The above code will run only once after the first time the component renders. Thereafter the component will re-render because the state of value will be changed but useEffect won’t run again as the dependency does not change with respect to the previous execution of the cycle.

Let’s now see another important functionality provided by the useEffect hook

Clean up function in useEffect

useEffect(() => {
console.log(“effect”);
return () => { console.log(“clean up”)};
}, []);

return() => {} is the clean up function provided by useEffect hook.

When does this function run ?

Whenever the effect runs, the clean up function runs before the useEffect function (except for the very first time) and it also runs whenever the component unmounts.

In simple words —

i) Clean up function does run before the very time, side effect function runs in the useEffect (i.e when the component renders first time).

ii) It runs before every new side Effect function execution in the useEffect hook (i.e the component re-renders and useEffect runs because of change in the value of any of the dependencies)

iii) It runs whenever the component unmounts from the DOM.

Let’s take a closer look:

You will see in the console —

  • When the component renders, “effect” will be logged. You won’t see “clean up” in the console as it does not execute for the very first time.
  • Thereafter if you click the button, first “clean up” will be logged and then “effect” will be logged. This means after the component re-renders useEffect is triggered and the clean up function runs before the effect function.
  • If you unmount this component from the parent component by some means you will see “clean up” logged in the console, but you won’t see “effect” as only the clean up function executes after the component unmounts.

Hope this blog helps you to learn and understand the useEffect hook more concisely. If you like it please give a clap, it will really motivate me to continue writing such blogs. Moreover, positive criticism is always welcome. If you think of some improvement in the blog your suggestions will be appreciated.

--

--