Understanding React useEffect
Tags: react, hooks, javascript, frontend
Published: 2021-09-13 21:46:05

If you are new to React or you used React with class based components, you are probably trying to understand how exactly `useEffect` works, when to use and how to use it.

But before we show any code, we need to understand that a React component has a few various lifecycle events, and the main ones are the following:

* A component is created
* A component, or it's state, changes
* A component is destroyed

And React has some very hand hooks that you can use to 'hook' to those events (sorry for the pun).

### The `useEffect` hook

Now before we see how the hook works in practice, we need to see how this function is expecting from us, the developer, to be called.

The `useEffect(callback, [dependencies])` expects a callback function as a first argument, and this **must not** be an async function, to essentially define what to execute when the hook is invoked.

The second argument, that can be optional, represents the dependencies, and the dependencies are essentially a list of state variables that we want to listen to, but I'll explain later in more detail.

But let's see how it works.

### A component is created

When a component is added to the virtual DOM, and it's rendered for the first time, this is when we can say it has been *created*.
If you used React in the past, to handle compoment events, you probably used something like this code:

```javascript
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: ''
    };
  }

  // component is mounted
  componentDidMount() {
    // do something
    this.setState({
      name: 'Fred'
    });
  }

  // component state changed
  componentDidUpdate(prevProps, prevState) {
    //do something
  }

  // component is destroyed
  componentDidUnmount() {
    // do something
  }

  render() {
    const { name } = this.state;
    return <div>My name is {name}</div>;
  }
}
```

So in the component above, we'll have our `state` to contain a variable called `name` that is empty at the very beginning, but right after the component is mounted, or created, will be set to `Fred`.

To do the exact same behaviour as above with the `useEffect` hook, using a functional component, our code will look like this:

```javascript
import { useEffect, useState } from 'react';

const MyComponent = () => {
  const [name, setName] = useState('');

  useEffect(() => {
    setName('Fred');
  }, []);

  return <div>My name is {name}</div>;
}
```

But let's explain this code a little bit.
As you see, we tell the `useEffect` to invoke a function that updates the state for the `name` state variable, but as a dependencies we pass an empty array.
With this hook, an empty array simply means "do it when the component mounts and only once".

So you might ask, why the need to pass an empty array? Why we don't simply pass nothing?

Because with the `useEffect` hook, if you pass no dependencies at all, it will call the callback function on every single render of the component, not only at the beginning, and that's something you normally want to avoid to to have the event handler to unnecessarily get detached and reattached on every time the component renders.

### A component is updated

If you want to listen to a state variable and see when it changes, here is the place where you want to pass the array of dependencies.

For example in this code we have a `firstname` and `lastname` variables, and we want to `console.log` the variable every time it changes, our code will look like this:

```javascript
import { useEffect, useState } from 'react';

const MyComponent = () => {
  const [firstname, setFirstname] = useState('Fred');
  const [lastname, setLastname] = useState('Flinstone');

  useEffect(() => {
    console.log(firstname)
  }, [firstname]);

  const handleFirstname = newName => setFirstname(newName);
  const handleSurname = newName => setLastname(newName);

  return (
    <div>
      <p>My name is {firstname} {lastname}</p>
      <a onClick={() => handleFirstname('Barney')}>Change firstname</a>
      <a onClick={() => handleSurname('Rubble')}>Change lastname</a>
    </div>
  );
}
``` 

In the code above we set the initial firstname to be "Fred", but when we click on the component it changes to "Steve", and our hook will listen to the `firstname` to change, running the callback only when that variable changes, and not when any other ones do, like the `lastname` one.

And you can also use multiple `useEffect` in your component, for example if we want one hook each for our variables, we can something like this:

```javascript
useEffect(() => {
  // do something when firstname changes
}, [firstname]);

useEffect(() => {
  // do something when lastname changes
}, [lastname]);
```

### A component is destroyed

The last lifecycle event I want to show you is the one invoked when a component is destroyed, or removed by the virtual DOM.
This is rather simple, and essentially all you need to do, is to return a function inside the callback of the `useEffect`.

Let's say that you want to do some stuff when you create the component, like reading some data from a datasource, and when you destroy the component you just want to get rid of it, your hook will look like this:

```javascript

import { useEffect, useState } from 'react';

const MyComponent = () => {
  const [data, setData] = useState({});

  useEffect(() => {
    // fetch some data from somewhere and add it to the state
    setData(someStuff);
    return () => {
      // just empty the data
      setData({});
    }
  }, []);
}
```

And again, this can be done with dependencies as well if you need to.

The above lifecycle event is not used extremely often, but it can be handy in some occasions, like telling a websocket connection to disconnect for example.

I hope that this post was useful for you to better understand how to use the React useEffect hook.