Lessons learned working with React Hooks and Closures
Tags: react, javascript, beginners
Published: 2020-11-06 08:00:30

---
title: Lessons learned working with React Hooks and Closures
published: true
description: A quick introduction and lessons learned using hooks.
tags: react, javascript, beginners
cover_image: https://images.unsplash.com/photo-1485700073848-c87ddfa57e02?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1650&q=80
---

In this post, I will: 

- Give a quick introduction to hooks
- Share some pitfalls, lessons learned working with hooks
- Share awesome resources for diving deeper

If you want to play with the code samples then open [https://codesandbox.io/s/new](https://codesandbox.io/s/new) and paste them in as you go.

## What are hooks?

> A hook is a function provided by React that lets you hook into React features from your function components - Dan Abramov

React hooks make components simpler, smaller, and more re-usable without using [mixins](https://reactjs.org/blog/2016/07/13/mixins-considered-harmful.html).

React Hooks were released with React 16.8, February 2019 which in tech makes them quite old at this point 😊 Hooks have made a radical shift in how components are being developed. Before, the recommendation was to use Class components **and** Pure functional components, components without state only props. 

This meant you may have started writing a Pure component, only to find out you needed state or lifecycle methods, so you had to refactor it into a class.

Introduce hooks. Hooks allow functional components to use all of React's features. But what is even more powerful is it allows components to separate visual render logic and "business" logic.

## Your first hook - `useState`

useState allows a functional component to... well... use state 😄

Let's see an example:

```jsx
function Counter() {
  const [count, setCount] = useState(0)
  return (
    <button onClick={() => setCount(count + 1)}>
      {count}
    </button>
  )
}
```

### But how can a function keep track of state?

If you're sharp, then you may ask yourself straightaway "How can a function keep track of state?". How does a simple variable in a function replace a class field?

Remember, when using classes React only has to call the `render()` function but with a function component it calls the entire function again, so how is state kept between renders?

Here's a class component as a refresher:

```jsx
class Counter extends Component {
  constructor() {
    this.state = { count: 0 }
  }
  render() {
    return (
      <button
        onClick={this.setState({
          count: count + 1,
        })}
      >
        {count}
      </button>
    )
  }
}
```

Hooks have to be run in the same order every time, this means no hooks inside of if statements! Conceptually, you can think of hooks as being stored in an array where every hook has its own index as a key. So the value of our `count` variable above would be `hookArray[countHookIndex]`.

Without help, this would be an easy mistake to make which is why React has published [a couple of ESLint rules](https://reactjs.org/docs/hooks-rules.html#eslint-plugin) to help us.

Let's dive into where most mistakes happen, the `useEffect` hook.

## Side effects with hooks - `useEffect`

What do I mean by side effects? Things such as:

- Fetching data on mount
- Setting up event listeners
- Cleaning up listeners on dismount

Here's an example of setting up an event listener "on mount":

```jsx
useEffect(() => {
  const handleKeyUp = e => {
    if (e.key === 'j') {
      alert('You pressed j')
    }
  }
  document.addEventListener(
    'keyup',
    handleKeyUp
  )
  return () => {
    document.removeEventListener(
      'keyup',
      handleKeyUp
    )
  }
}, [])
```

Why is unmount in quotes? Because there are no hooks matching the lifecycle methods such as `componentDidMount()` there's an entirely new way of thinking with `useEffect`.

The second parameter of `useEffect` is what is called a **dependency array**. Since I've added an empty array, the code is run once (on mount), because the dependencies never change.

If I omitted the argument, the code would run *on every render and update.*

The React team noticed that setting up and removing listeners is part of the same abstraction and thus the code should be co-located therefore when an effect returns a function it will be run in the cleanup phase, that is, between renders.

While confusing at first, this is extremely powerful. **You can add state variables to the dependency array!** Essentially allowing you to "watch" state variables.

## Dependency array pitfalls

Luckily, most of the pitfalls can be caught by using the [ESLint rules](https://reactjs.org/docs/hooks-rules.html#eslint-plugin) from earlier. But it is good to understand *why,* such that, when you encounter a problem the plugin did not account for, you can solve it yourself.

I should also mention there are a few other hooks that also use dependency arrays: `useMemo` and `useCallback` but I won't cover these here.

What I failed to think about for a long time was that you're passing a function to `useEffect` and that function has a closure over your functional component's state and other variables. I will explain closures in a bit, but let's first see an example of how this goes wrong:

```jsx
function Counter() {
  const [count, setCount] = useState(0)
  useEffect(() => {
    const timer = setInterval(
      () => console.log('count', count),
      1000
    )
    return () => clearInterval(timer)
  }, []) // ESLint warns us we're missing count
  return (
    <button onClick={() => setCount(count + 1)}>
      {count}
    </button>
  )
}
```

Now, this is a really contrived example but the above example will log `count 0` every second regardless of how many times the user presses the button. This is because arrow function passed to `useEffect` is created on mount and never again thus `count` will remain `0` because it is a closure over the first "instance" of the function. We have a stale closure.

### What is a closure?

The simplest definition I've heard is a closure allows a function to keep private variables.

Let's see an example:

```jsx
function createCounter() {
  let count = 0
  return () => {
    count += 1
    return count
  }
}

const counter = createCounter()
counter() // 1
counter() // 2
```

Now if you want to create just one counter you can wrap it in a module, an IIFE - immediately invoked function expression:

```jsx
const counter = (() => {
  let count = 0
  return () => {
    count += 1
    return count
  }
})()

counter() // 1
counter() // 2
```

That's a lot of functions 🤯 Let's break it down:

- `createCounter()` was a function that returned a function.
- So we let `createCounter` call itself straight away, creating the closure for the `count`. That hides `count` from the outer scope.

If you were a developer during the jQuery days this will seem very familiar to you. IIFE's were the go-to way of creating modules, avoiding having everything in the global scope, since CommonJS (require and module.exports) and ECMAScript Modules, ESM (import/export) were not created yet.

## Dive deeper

- Swyx creating a simple React from scratch
    - [https://www.youtube.com/watch?v=KJP1E-Y-xyo](https://www.youtube.com/watch?v=KJP1E-Y-xyo)
- Kent Dodds accordion video
    - [https://www.youtube.com/watch?v=AiJ8tRRH0f8](https://www.youtube.com/watch?v=AiJ8tRRH0f8)
- Tanner Linsley custom hooks video
    - [https://www.youtube.com/watch?v=J-g9ZJha8FE](https://www.youtube.com/watch?v=J-g9ZJha8FE)
- Kent Dodds on hook pitfalls
    - Blog: [https://kentcdodds.com/blog/react-hooks-pitfalls](https://kentcdodds.com/blog/react-hooks-pitfalls)
    - Video: [https://www.youtube.com/watch?v=VIRcX2X7EUk](https://www.youtube.com/watch?v=VIRcX2X7EUk)
- useHooks library
    - [https://github.com/gragland/usehooks](https://github.com/gragland/usehooks)

I hope you enjoyed this quick introduction to hooks. If you have any questions, feel free to comment below!