I used effects, said goodbye to my lifecycles, and feel 🙂
Tags: react, hooks, javascript, learning
Published: 2019-02-11 18:44:23

---
title: I used effects, said goodbye to my lifecycles, and feel 🙂
published: true
description: My first journey into using effects in React Hooks. I liked it!
tags: react, hooks, javascript, learning
---
Last week Hooks were finally added to React. It is now possible to use `state` in a function. At least, that much I remembered from watching the initial announcement way back when. But lifecycles though... I had no idea how _they_ were supposed to work. I decided to read [the docs](https://reactjs.org/docs/hooks-intro.html) and experiment.

## Let's experiment
Let's try and build a little app that keeps a few bits of `state` and does things based on `state` changes. Normally I would look to my beloved `componentDidUpdate` for this, but she's gone... well not really: `classes` will very much remain part of React, but still... I might have already said goodbye to them.

Okay, the 'app'. Let's create a numerical `input` field with an `onChange` handler, a `button` to add said `input` to whatever the `input` was before, and finally a `button` to switch from dark to light mode and vice-versa (somehow everyone seems to love that).

The initial state for that in our Hookian Age would look a little something like this:

```javascript
function App() {
  const [lights, setLights] = useState(true);
  const [input, setInput] = useState(0);
  const [sum, setSum] = useState(0);
}
```
Next up we're going to hook it up to HTML elements, just like we'd do in our `classes`.

```javascript
return (
    <div
      style={{
        padding: 15,
        fontFamily: "Arial",
        background: lights ? "salmon" : "black",
        color: lights ? "black" : "white"
      }}
    >
      <h1
        style={{ fontFamily: "Times New Roman", margin: 0, paddingBottom: 15 }}
      >
        Sumple UseEffect
      </h1>
      <div>
        <strong>Sum:</strong> {sum}
      </div>
      <div style={{ marginTop: 15 }}>
        <input
          type="number"
          value={input}
          onChange={e => setInput(e.target.value)}
          style={{ width: 50 }}
        />
        <button onClick={handleSum}>
          +
        </button>
      </div>
      <div style={{ position: "absolute", top: 30, right: 30 }}>
        <button onClick={() => setLights(!lights)}>
          Lights {lights ? "off" : "on"}
        </button>
      </div>
    </div>
  );
```
Hopefully this doesn't look weird to you. The only thing that's missing from the code is the `handleSum` function. It looks like this:

```javascript
  function handleSum() {
    setSum(Number(sum) + Number(input));
    setInput(0);
  }
```
It's defined within our `App` function and simply adds the `input` state to our `sum` state; and then sets the `input` back to zero. We should have a working app now. So far, so good.

## We Use Effects
Now we've decided we want to validate the numerical input. If the value is 0 or above a very arbitrarily chosen number of 480, we're going to disable the `+` button. Granted, we don't actually need an extra piece of state for this, but let's just do it anyway.

We create `valid` as a piece of state.

```javascript
  const [valid, setValid] = useState(true);
```
and then we add `disabled` to our `button`.

```javascript
  <button disabled={!valid} onClick={handleSum}>
     +
  </button>
```
If we were still using `classes` we could have done something like this.

```javascript
componentDidUpdate(prevProps, prevState) {
  if(prevState.input !== this.state.input) {
     if (this.state.input > 480 || this.state.input === 0) {
        this.setState({valid: false});
      } else {
        this.setState({valid: true});
      }
  }
}
```
But we're not. So we don't have `componentDidUpdate`. Instead we do have `useEffect`, a function that React promises to run after each `render`. That's convenient, because each change in `input` triggers a `render`. So we can use `useEffect` to do our validation.

```javascript
  useEffect(
    () => {
      if (input > 480 || input === 0) {
        setValid(false);
      } else {
        setValid(true);
      }
    },
    [input]
  );

```
Our new friend accepts a function as a first argument, and an array as an optional second argument. I hope that the function makes sense, but I would understand if you have questions about the lack of comparing `state` and the `array`. Luckily, those two are connected.

It turns out, you don't have to compare `props` or `state` anymore. React allows you to provide the values in the second argument you want to run `useEffect` on. So in our case `useEffect` will only run on changes in `input`.

##Did we mount?
So we now have a way to 'do stuff' based on changes in `props` or `state`. But how about that initial lifecycle that used to help us set up subscriptions? Or that one that allowed us to clean them up again? I'm talking about `componentDidMount` and `componentWillUnMount` of course.

We need a way to run `useEffect` regardless of changes, but not on every change. Preferably only once in the beginning, and once at the end. Luckily, that's very much possible.

```javascript
  function handleEscape(e) {
    if (e.keyCode === 27) {
      setSum(0);
      setInput(0);
    }
  }

  useEffect(() => {
    document.addEventListener("keydown", handleEscape);
    return () => document.removeEventListener("keydown", handleEscape);
  }, []);
```
If we leave the `array` empty in our second argument, React runs our code only once, and treats our `return` as a cleanup function for when our component unmounts. In our case we can use it to add a listener to reset our `sum` and `input` when someone presses `escape`.

But what about our other `useEffect`? Do we have to merge them...? 

The answer is NO! You can use multiple `useEffects` within the same component. Which is great, because we're going to add even more. I'm very curious to see how many times I change the `lights` or `sum`.

So we set up another `useEffect` and fill our `array` with `lights` and `sum`. Now this effect will only run when either `lights` or `sum` changes. It's good, but I just realized I want to know which of the two changed so I can keep more detailed stats.

The answer is actually to make two `useEffects`, but I dream back to the days (yesterday) when I still used `classes` and want to do a comparison between previous and current values, so I find out myself which one changed. How to get those previous values though? The React docs provide us with a helping hook.

```javascript
function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}
```
It's used like this:

```javascript
  const previousValues = usePrevious({ lights, sum });
```
So what's happening is that we pass our state values into the function, they are then saved in a `ref` and that ref is returned. This ensures we do not get the latest values, but the previous values. That might seem much more difficult then it needs to be, but remember that we could have just used two `useEffect` functions each with the other value in the second argument.

Let's complete our single `useEffect` though:
```javascript
  useEffect(
    () => {
      if (previousValues && previousValues.lights !== lights) {
        const newActions = Object.assign({}, actions, {
          lights: actions.lights + 1,
          total: actions.total + 1
        });
        setAction(newActions);
      } else if (previousValues && previousValues.sum !== sum) {
        const newActions = Object.assign({}, actions, {
          sum: actions.sum + 1,
          total: actions.total + 1
        });
        setAction(newActions);
      }
    },
    [lights, sum]
  );
```
That works. We now have a log of which actions have been used. Let's render it on the page.

```javascript
  <div>
    <strong>Actions:</strong> {actions.total}
  </div>
```
Have we exhausted our `useEffect` trickery? We have not. How about one that runs on each render? That would allow us to count how many renders we see right? Could be interesting. Let's do it.

But wait. Even if we have a `useEffect` that runs after each render, how will we save our amount of renders without triggering another render? We can `useRef` for that.

```javascript
  const renders = useRef(0);
```
We initialize the `ref` with 0. Then we create another effect.

```javascript
  useEffect(() => {
    renders.current = renders.current + 1;
  });
```
Since the `current` property of a `ref` is mutable, we can assign our new value to it. But did you notice how our `useEffect` doesn't have a second argument? I think I mentioned it before, but the second argument is optional. If you do not provide it, the `useEffect` runs on each render. Perfect for this case.

All that's left is to render our render count on the screen.

```javascript
  <div>
    <strong>Renders:</strong> {renders.current}
  </div>
```
Yes, you can just render the `current` value of the `ref`. Awesome!

## Am I happy?
After writing this app I wonder if I'm happy with all of this. To be honest, I think I am. While `useEffect` takes a bit of getting used to, it's very versatile. That might also be its 'problem'. Although, once you really start using `useEffect` you may not find that a problem at all.

It probably will take a bit more time before I get really comfortable with Hooks and all that they bring with them, but I already feel their potential. If you have the space and time to experiment with them, I strongly recommend to do so. There's no hurry, but they're cool. They really are 🙂.


[Full code](https://codesandbox.io/s/0p1wx7vxqv) if you are interested.