How not to useEffect
Tags: javascript, react, webdev, hook
Published: 2024-03-01 22:09:28

The first thing that we have to understand is:
&nbsp;
## Effects lifecycle are not the same thing as the Component's lifecycle
&nbsp;
> Components may mount, update, or unmount. An Effect can only do two things: to start synchronizing something, and later to stop synchronizing it. This cycle can happen multiple times if your Effect depends on props and state that change over time. [react.dev](https://react.dev)

&nbsp;

Effect's only run after the render. This information alone already discards the idea that we can use `useEffect` to run some code before the render (something like the old `componentWillMount`). 
&nbsp;
- **Effect's will always run after the render.**
- **Also, effect's will only cause a rerender if you change an state** (this case is a bad use, will talk about in a sec)

&nbsp;

In this post I want to focus on the bad use of useEffect.

&nbsp;

Let's take this very simple but common example below: 

```typescript
const [firstName, setFirstName] = useState<string>("John");
const [lastName, setLastName] = useState<string>("Stewart");

const [fullName, setFullName] = useState
<string>("");

useEffect(() => {
  setFullName(firstName + " " + lastName);
}, [firstName, lastName]);
```

React will run the following order of actions:

1. Will store the value "John" to the firstName state;
2. Will store the value "Stewart" to the lastName state;
3. Will store an empty string to the fullName state;
4. With all the calculations finished, will render the component;
5. Then will call the effect considering the array of dependencies [firstName, lastName];
6. The effect will schedule the change for the fullName state;
7. Will start a new render (_caused by setFullName_), going through firstName and lastName with the same values;
8. Then will store the value "John Stewart" to the fullName state;
9. Finally will render the component with all the information;

&nbsp;

Describing all the steps makes easy to see the problem: We already have all the information that we need on the first render, so why render twice?

&nbsp;

Instead wee can do something much simpler, removing the state for fullName and the effect:

```typescript
const [firstName, setFirstName] = useState<string>("Taylor");
const [lastName, setLastName] = useState<string>("Swift");

const fullName: string = firstName + " " + lastName;
```

Let's list it out the steps again:

1. Will store the value "John" to the firstName state;
2. Will store the value "Stewart" to the lastName state;
3. Will store the value "John Stewart" to the fullName const;
4. With all the calculations finished, will render the component;

That's it!

If the firstName and/or lastName states change at some point, the component will rerender by itself and then store the new value in fullName.

For more bad examples of `useEffect` use, you can look at the react docs article here: https://react.dev/learn/you-might-not-need-an-effect
&nbsp;
&nbsp;
## Let's see some good uses of Effects:

useEffect was created so we can synchronize with external systems. Like a browser API, set up a server connection, or send an analytics log. **All these things happen after the render**.

Let's look at this page that renders a Video.

- First we have the page:

```typescript
export default function App() {
  const [isPlaying, setIsPlaying] = useState(false);
  return (
    <>
      <button onClick={() => setIsPlaying(!isPlaying)}>
        {isPlaying ? "Pause" : "Play"}
      </button>
      <VideoPlayer
        isPlaying={isPlaying}
        src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
      />
    </>
  );
}
```
&nbsp;

So here we have a page with the `isPlaying` state, that allows the user to control the video with a button.

&nbsp;

- And here we have the Video component:

```typescript
function VideoPlayer({ src, isPlaying }) {
  const ref = useRef(null);

  useEffect(() => {
    if (isPlaying) {
      ref.current.play();
    } else {
      ref.current.pause();
    }
  });

  return (
    <>
      <video ref={ref} src={src} loop playsInline />
    </>
  );
}
```
&nbsp;

In this component, we have:
- A ref for the HTML video tag, so we can access all its methods, properties, and events;
- An effect that calls the method play() or pause() based on `isPlaying`'s value.

&nbsp;

The user can click multiple times on the Play/Pause button and the video player will stay synchronized to the `isPlaying` value.

&nbsp;

So, we could see a simple but objective example on how we can use Effects to synchronize our component with a external system.