Use React Hooks Correctly with These Two Rules
Tags: react, javascript, webdev, tutorial
Published: 2019-08-14 23:41:12

Hooks were introduced at React Conf 2018, and have been steadily gaining popularity as an easy way to introduce state to functional react components.

It's important to note that **you don't have to use hooks** if you don't want to - but if you do, make sure you follow these two rules of hooks!  Otherwise - bad things might happen.

# Why Hooks Were Created

First, let's take a look at the problem that hooks are trying to solve. 

Here's a React class component with a single bit of state called `count`, that counts up when the button is clicked:

```jsx
// OLD WAY: Class Components

class Counter extends React.Component {
  
  state = {
    count: 0
  }

  render() {
    return (
      <div>
        <button 
          onClick={() => 
            this.setState({ count: this.state.count + 1 })
          }
        >
          +1
        </button>
        <p>Count: {this.state.count}</p>
      </div>
    )
  }
}
```

That works just fine, but there are a few parts that can easily create bugs.

## Problem 1: What is the meaning of `this`?

One of the most confusing parts of JavaScript for new developers is that the `this` keyword changes meaning based on the context.  This includes the very important, but (seemingly) arbitrary way that you define your functions.

For example, this function:

```jsx
// probably not what you want
function onClick() {
  this.setState({ this.state.count: count + 1 })
}
```

doesn't bind `this` to the class component - and so probably won't behave like you want!  Instead, you have to remember to either bind that function to the class - or use an arrow function:

```jsx
// probably what you want
const onClick = () => {
  this.setState({ this.state.count: count + 1 })
}
```

Until you get a really good feeling for what the meaning of `this` is in various parts of a class component, it can cause subtle, and really confusing bugs.

**Hooks simplify that** by removing the need to track the meaning of `this` altogether.  This is because there is no class that you have to reference (since everything is functional components with hooks).


## Problem 2: Using `this.state...` to access data and calling `this.setState` to change it

The fact that `state` lives on the class component means that anytime you want to access a value in state, you have to preface it with `this.state`.  This can be confusing to beginners - but also can bite experience programmers.

As a testament to how annoying it can be - when creating the demo for this article, I originally typed the following onClick function:

```jsx
// Can you spot the bug?
...
  this.setState({ count: count + 1 })
...
```

Do you see the bug?  Yep; I forgot to say `this.state.count: ` instead of just `count: ` in the `setState` call.  That didn't cause a render error or anything - but it just didn't work; and it took me some debugging to figure out what went wrong... *annoying!*

**Hooks simplify that** by removing the concept of class `state`, and just giving access to the values and set functions directly.  No more `this.state`!

## Problem 3: People are using functional components more and more

Since functional components are *'just functions'*, and are generally easier to type and to reason about - more and more people are defaulting to functional components over class components.

The problem is, that as soon as you want to add state to a functional component - you have to convert it to a class component, or bring in a more complicated library like Redux.

**Hooks simplify that** by giving you a way to add state to functional components directly with the `useState` hook.

Then - with the `useEffect` hook, you can replicate the lifecycle methods of class components, and suddenly - *you don't need classes anymore!*

So you can just keep using your functional components everywhere now.

# How Hooks Work

Ok - let's take a look at the same `Counter` example; but this time with hooks:

```jsx
// NEW WAY: Hooks
import React, { useState } from "react";
...
const Counter = () => {
  const [count, setCount] = useState(0)

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>
        +1
      </button>
      <p>Count: {count}</p>
    </div>
  )
}
```

Instead of setting initial state with `state = {}`, we use the `useState` hook to define a new bit of state that we call `count`, which defaults to `0`.

Importantly (for the rules later) - React stores this value **internally in an array** of all the values created with hooks in this function.  We only have one here; but let's add another to see what that looks like:

```jsx
// Multiple states

const Counter = () => {
  const [count, setCount] = useState(0)
  const [name, setName] = useState('Chris')
  ...
}
```

So with two `useState` calls above, we know that React is storing an array of two values.

OK!  Now we have all the information we need in order to **understand the rules of hooks**. 

# The Rules of Hooks

## Rule 1: Call hooks *unconditionally* at the top level of your component

Because hooks are stored in an array, the *order that they get called matters.*  Since React components are re-rendered each time data changes, that means that the **exact same hooks** must be called in the **exact same order** on every single render.

That means if you added an `if` statement in there:

```jsx
// DON'T DO THIS!
...
if(myBool) {
  const [count, setCount] = useState(0)
}
const [name, setName] = useState('Chris')
...
```

The `count` state would sometimes be created - and sometimes wouldn't be.  But React can't track that internally - so it would mess up the value of all the state stored in the array for that component.

Also - don't put hooks inside of loops (or any other control function):

```jsx
// ALSO DON'T DO THIS!
...
for(my conditions) {
  ...useState...
  ...useEffect...
}
...
```

... because if your conditions are based on variables that change, then you'll have hooks run in different orders from render to render.

## Rule 2: Only call hooks in React functions or custom hooks

This rule exists for the same reason at rule #1, but is slightly different.

For example, if we created a helper function called `doSomething`:

```jsx
// DON'T DO THIS

function doSomething() {
  const [count, setCount] = useState(0)
  ... do something with count ...
}
```

Then you (or another developer) may not realize that that `doSomething` function actually calls a hook - and may be tempted to call `doSomething` out of order:

```jsx
// MISUSE of doSomething

const Counter = () => {
  return <button onClick={doSomething}>Do it!</button>
}
```

Which breaks the hooks!  

It breaks them because React internally can't keep track of hooks that run out of order (same as rule #1) - so **only use hooks at the top of react components, or in custom hooks that you create**.

# Hooks Aren't Scary

Hooks solve a few common problems that developers have had with React class components.  You don't have to use them (so don't go out and replace a bunch of your code *'just because'*) - but if you do, then follow the rules:

1. **Call hooks unconditionally at the top level of your component**
2. **Only call hooks in React functions or custom hooks**

And that's it!  

Once you know that React keeps hook values in arrays, then it makes sense: don't change the order of the hooks that are called, or else React can't keep track of what's what!


This post originally posted on: https://chrisachard.com/use-react-hooks-correctly-with-these-two-rules