Home

React Hooks useEffect()

We import the useEffect() hook from the react library by adding the code
import React, {useEffect } from 'react';
or...if you also want the useState() hook as well then...
import React, {useState, useEffect } from 'react';

The Effect Hook is used to call another function that does something for us so there is nothing returned when we call the useEffect() function.

There are three key moments when the Effect Hook can be utilized:

When the component is first added, or mounted, to the DOM and renders.
When the state or props change, causing the component to re-render.
When the component is removed, or unmounted, from the DOM.

Then when you want to use the useEffect() command you could, eg...

useEffect( () => {
   alert(`Counter: ${myCount}`)
}
);


We are calling the useEffect
with a callback function () => {...}
which tells it to display an alert = Counter: 0

Clean Up Effects

Some effects require cleanup. For example, we might want to add event listeners to some element in the DOM, beyond the JSX in our component.
When we add event listeners to the DOM, it is important to remove those event listeners when we are done with them to avoid memory leaks!

Let’s consider the following effect:

useEffect(()=>{
    document.addEventListener('keydown', handleKeyPress);
    return () => {
        document.removeEventListener(
            'keydown', handleKeyPress);
    };
})
If our effect didn’t return a cleanup function
(see return () => {...}), then a new event listener would be added to the DOM’s document object every time that our component re-renders.
Not only would this cause bugs, but it could cause our application performance to diminish and maybe even crash!

Because effects run after every render and not just once, React calls our cleanup function before each re-render and before unmounting to clean up each effect call.

If our effect returns a function, then the useEffect() Hook always treats that as a cleanup function.
React will call this cleanup function before the component re-renders or unmounts.
Since this cleanup function is optional, it is our responsibility to return a cleanup function from our effect when our effect code could create memory leaks.

Top

Example code

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

export default function Counter() {
    const [clickCount, setClickCount] = useState(0);

    const increment = () => {
        setClickCount((prevClickCount) => 
            prevClickCount + 1);
    } 

    useEffect( () => {
        document.addEventListener('mousedown', increment);
        return () => {
            document.removeEventListener(
                'mousedown', increment);
        }
    });

     return (
        <h1>Document Clicks: {clickCount}</h1>
    );
}

In the code I created a function called increment which gets called on a mousedown event handler. This increment function uses a state setter callback function (setClickCount) to add 1 to clickCount.
The setClickCount automatically updates clickCount as this is all part of the useState() hook that we set up .

The useEffect() hook to add a mousedown event listener to the document, so when you perform a mousedown anywhere on the form it fires the call to the increment function.

NOTE
Because of where the useEffect() is placed it gets run on every render.
And because you must not keep adding event listeners every time the screen renders you will need to remove the event listener too.
(so this will add the mousedown listener - perform the increment - then remove the mousedown listener. Thus preventing memory leaks/too many mousedown listeners)

.

Another Example Code

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

export default function Timer() {
  const [time, setTime] = useState(0);
  const [name, setName] = useState("");

  useEffect(() => {
    const intervalId = setInterval(() => {
      setTime((prev) => prev + 1);
    }, 1000);

    return () => {
      clearInterval(intervalId);
    };
  }, []);

  const handleChange = 
       ({ target }) => setName(target.value);

  return (
    <>
      <h1>Time: {time}</h1>
      <input 
        value={name} 
        onChange={handleChange} 
        type='text' />
    </>
  );
}

The bit of code on the end of the useEffect() hook
, []
is called an 'empty dependency array'.

See next pane for more on the dependency array.

Top

Control when Effects are called

Dependency Array

It is common, when defining function components, to run an effect only when the component mounts (renders the first time), but not when the component re-renders.
The Effect Hook makes this very easy for us to do! If we want to only call our effect after the first render, we pass an empty array to useEffect() as the second argument.
This second argument is called the dependency array.

The dependency array is used to tell the useEffect() method when to call our effect and when to skip it.
Our effect is always called after the first render but is only called again if something in our dependency array has changed values between renders .

useEffect(() => {
    const intervalId = setInterval(() => {
      setTime((prev) => prev + 1);
    }, 1000);

    return () => {
      clearInterval(intervalId);
    };
  }, []);
The second argument is the empty dependency array...
, []


More on this can be found in this document

Control when Effects are called (pt2)

In the following bit of code the Effect will be run on the FIRST RENDER
But will then only be re-run when the value of count changes.

Because count is passed to useEffect() as the second parameter it will ONLY get re-run when the value of count changes.

useEffect(
    () => {
        document.title = `You clicked ${count} times`;
    }
    , [count]
); 

First parameter in red
Second parameter in green


Top

Rules of Hooks

There are two main rules to keep in mind when using Hooks:

Only call Hooks at the top level
Only call Hooks from React functions


So DO NOT do this sort of thing

if (userName !== '') {
  useEffect(() => {
    alert("this is my code");
  });
}

INSTEAD you should do this...

useEffect(() => {
  if (userName !== '') {
    alert("this is my code");
  }
});
Which performs the check of the value of userName inside the useEffect hook statement.

Top