Why React Hooks?
Tags: react, javascript
Published: 2019-07-30 16:02:34

---
title: 'Why React Hooks?'
description: "When React Hooks were released, React was the most popular and most loved front-end framework in the JavaScript ecosystem. In this post, you'll learn why, despite existing praise, the React team dedicated so many resources to creating and releasing Hooks. Along the way, you'll also get a soft introduction to the main aspects of the Hooks API."
tags: "react, javascript"
canonical_url: "https://ui.dev/why-react-hooks/"
published: "true"
cover_image: "https://tylermcginnis.com/static/5496edc380b0d61fc6e0239d0cb08a7f/b2d08/why-react-hooks.jpg"
---

_This was originally published at [ui.dev](https://ui.dev/why-react-hooks/) and is part of our [React Hooks](https://ui.dev/react-hooks/) course. If you enjoy this post, check it out._

***

{% youtube eX_L39UvZes %}

The first thing you should do whenever you're about to learn something new is ask yourself two questions -

1) Why does this thing exist?
2) What problems does this thing solve?

If you never develop a convincing answer for both of those questions, you won't have a solid enough foundation to build upon when you dive into the specifics. These questions are specifically interesting in regards to [React Hooks](https://tylermcginnis.com/courses/react-hooks/). React was the [most popular and most loved front-end framework](https://insights.stackoverflow.com/survey/2019) in the JavaScript ecosystem when Hooks were released. Despite the existing praise, the React team still saw it necessary to build and release Hooks. Lost in the various Medium posts and blog think pieces on Hooks are the reasons __(1) why__ and for what __(2) benefit__, despite high praise and popularity, the React team decided to spend valuable resources building and releasing Hooks. To better understand the answers to both of these questions, we first need to take a deeper look into how we've historically written React apps.

### createClass

If you've been around the React game long enough, you'll remember the `React.createClass` API. It was the original way in which we'd create React components. All of the information you'd use to describe the component would be passed as an object to `createClass`. 

```jsx
const ReposGrid = React.createClass({
  getInitialState () {
    return {
      repos: [],
      loading: true
    }
  },
  componentDidMount () {
    this.updateRepos(this.props.id)
  },
  componentDidUpdate (prevProps) {
    if (prevProps.id !== this.props.id) {
      this.updateRepos(this.props.id)
    }
  },
  updateRepos (id) {
    this.setState({ loading: true })

    fetchRepos(id)
      .then((repos) => this.setState({
        repos,
        loading: false
      }))
  },
  render() {
    const { loading, repos } = this.state

    if (loading === true) {
      return <Loading />
    }

    return (
      <ul>
        {repos.map(({ name, handle, stars, url }) => (
          <li key={name}>
            <ul>
              <li><a href={url}>{name}</a></li>
              <li>@{handle}</li>
              <li>{stars} stars</li>
            </ul>
          </li>
        ))}
      </ul>
    )
  }
})
```

__[💻 Play with the code.](https://codesandbox.io/s/why-react-hooks-createclass-iw6s5)__

`createClass` was a simple and effective way to create React components. The reason React initially used the `createClass` API was because, at the time, JavaScript didn't have a built-in class system. Of course, this eventually changed. With ES6, JavaScript introduced the `class` keyword and with it a native way to create classes in JavaScript. This put React in a tough position. Either continue using `createClass` and fight against the progression of JavaScript or submit to the will of the EcmaScript standard and embrace classes. As history has shown, they chose the later.

### React.Component

> We figured that we’re not in the business of designing a class system. We just want to use whatever is the idiomatic JavaScript way of creating classes. - [React v0.13.0 Release](https://reactjs.org/blog/2015/01/27/react-v0.13.0-beta-1.html)

React v0.13.0 introduced the `React.Component` API which allowed you to create React components from (now) native JavaScript classes. This was a big win as it better aligned React with the EcmaScript standard. 

```jsx
class ReposGrid extends React.Component {
  constructor (props) {
    super(props)

    this.state = {
      repos: [],
      loading: true
    }

    this.updateRepos = this.updateRepos.bind(this)
  }
  componentDidMount () {
    this.updateRepos(this.props.id)
  }
  componentDidUpdate (prevProps) {
    if (prevProps.id !== this.props.id) {
      this.updateRepos(this.props.id)
    }
  }
  updateRepos (id) {
    this.setState({ loading: true })

    fetchRepos(id)
      .then((repos) => this.setState({
        repos,
        loading: false
      }))
  }
  render() {
    if (this.state.loading === true) {
      return <Loading />
    }

    return (
      <ul>
        {this.state.repos.map(({ name, handle, stars, url }) => (
          <li key={name}>
            <ul>
              <li><a href={url}>{name}</a></li>
              <li>@{handle}</li>
              <li>{stars} stars</li>
            </ul>
          </li>
        ))}
      </ul>
    )
  }
}
```

__[💻 Play with the code.](https://codesandbox.io/s/why-react-hooks-reactcomponent-xdhg5)__

Though a clear step in the right direction, `React.Component` wasn't without its trade-offs. 

##### constructor

With Class components, you initialize the state of the component inside of the `constructor` method as a `state` property on the instance (`this`). However, according to the ECMAScript spec, if you're extending a subclass (in this case, `React.Component`), you must first invoke `super` before you can use `this`. Specifically, when using React, you also have to remember to pass `props` to `super`. 

```js
  constructor (props) {
    super(props) // 🤮

    ...
  }
```

##### Autobinding

When using `createClass`, React would auto-magically bind all the methods to the component's instance, `this`. With `React.Component`, that wasn't the case. Very quickly, [React developers everywhere](https://stackoverflow.com/questions/32317154/react-uncaught-typeerror-cannot-read-property-setstate-of-undefined) realized they didn't know how [the __this__ keyword](https://tylermcginnis.com/this-keyword-call-apply-bind-javascript/) worked. Instead of having method invocations that "just worked", you had to remember to `.bind` methods in the class's `constructor`. If you didn't, you'd get the popular "Cannot read property `setState` of undefined" error.

```js
  constructor (props) {
    ...

    this.updateRepos = this.updateRepos.bind(this) // 😭
  }
```

Now I know what you might be thinking. First, these issues are pretty superficial. Sure calling `super(props)` and remembering to `bind` your methods is annoying, but there's nothing fundamentally wrong here. Second, these aren't necessarily even issues with React as much as they are with the way JavaScript classes were designed. Both points are valid. However, we're developers. Even the most superficial issues become a nuisance when you're dealing with them 20+ times a day. Luckily for us, shortly after the switch from `createClass` to `React.Component`, the [Class Fields](https://tylermcginnis.com/javascript-private-and-public-class-fields/) proposal was created. 

##### Class Fields

Class fields allow you to add instance properties directly as a property on a class without having to use `constructor`. What that means for us is that with Class Fields, both of our "superficial" issues we previously talked about would be solved. We no longer need to use `constructor` to set the initial state of the component and we no longer need to `.bind` in the `constructor` since we could use arrow functions for our methods.

```jsx
class ReposGrid extends React.Component {
  state = {
    repos: [],
    loading: true
  }
  componentDidMount () {
    this.updateRepos(this.props.id)
  }
  componentDidUpdate (prevProps) {
    if (prevProps.id !== this.props.id) {
      this.updateRepos(this.props.id)
    }
  }
  updateRepos = (id) => {
    this.setState({ loading: true })

    fetchRepos(id)
      .then((repos) => this.setState({
        repos,
        loading: false
      }))
  }
  render() {
    const { loading, repos } = this.state

    if (loading === true) {
      return <Loading />
    }

    return (
      <ul>
        {repos.map(({ name, handle, stars, url }) => (
          <li key={name}>
            <ul>
              <li><a href={url}>{name}</a></li>
              <li>@{handle}</li>
              <li>{stars} stars</li>
            </ul>
          </li>
        ))}
      </ul>
    )
  }
}
```

__[💻 Play with the code.](https://codesandbox.io/s/why-react-hooks-class-fields-9tbis)__

So now we're good, right? Unfortunately, no. The move from `createClass` to `React.Component` came with some tradeoffs, but as we saw, Class Fields took care of those. Unfortunately, there are still some more profound (but less talked about) issues that exist with all the previous versions we've seen. 

The whole idea of React is that you're better able to manage the complexity of your application by breaking it down into separate components that you then can compose together. This component model is what makes React so elegant. It's what makes React, React. The problem, however, doesn't lie in the component model, but in how the component model is implemented.

### Duplicate Logic

Historically, how we've structured our React components has been coupled to the component's lifecycle. This divide naturally forces us to sprinkle related logic throughout the component. We can clearly see this in the `ReposGrid` example we've been using. We need three separate methods (`componentDidMount`, `componentDidUpdate`, and `updateRepos`) to accomplish the same thing - keep `repos` in sync with whatever `props.id` is. 

```js
  componentDidMount () {
    this.updateRepos(this.props.id)
  }
  componentDidUpdate (prevProps) {
    if (prevProps.id !== this.props.id) {
      this.updateRepos(this.props.id)
    }
  }
  updateRepos = (id) => {
    this.setState({ loading: true })

    fetchRepos(id)
      .then((repos) => this.setState({
        repos,
        loading: false
      }))
  }
```

To fix this, we'd need a whole new paradigm for the way in which we'd handle side effects in React components. 

### Sharing Non-visual Logic

When you think about composition in React, odds are you think in terms of UI composition. This is natural since it's what React is so good at.

```md
view = fn(state)
```

Realistically, there's more to building an app than just the UI layer. It's not uncommon to need to compose and reuse non-visual logic. However, because React couples UI to a component, this can be difficult. Historically, React hasn't had a great answer for this. 

Sticking with our example, say we needed to create another component that also needed the `repos` state. Right now, that state and the logic for handling it lives inside of the `ReposGrid` component. How would we approach this? Well, the simplest approach would be to copy all of the logic for fetching and handling our `repos` and paste it into the new component. Tempting, but nah. A smarter approach would be to create a [Higher-Order Component](https://tylermcginnis.com/react-higher-order-components/) that encapsulated all of the shared logic and passed `loading` and `repos` as props to whatever component needed it.

```js
function withRepos (Component) {
  return class WithRepos extends React.Component {
    state = {
      repos: [],
      loading: true
    }
    componentDidMount () {
      this.updateRepos(this.props.id)
    }
    componentDidUpdate (prevProps) {
      if (prevProps.id !== this.props.id) {
        this.updateRepos(this.props.id)
      }
    }
    updateRepos = (id) => {
      this.setState({ loading: true })

      fetchRepos(id)
        .then((repos) => this.setState({
          repos,
          loading: false
        }))
    }
    render () {
      return (
        <Component
          {...this.props}
          {...this.state}
        />
      )
    }
  }
}
```

Now whenever any component in our app needed `repos` (or `loading`), we could wrap it in our `withRepos` HOC.

```js
// ReposGrid.js
function ReposGrid ({ loading, repos }) {
  ...
}

export default withRepos(ReposGrid)
```

```js
// Profile.js
function Profile ({ loading, repos }) {
  ...
}

export default withRepos(Profile)
```

__[💻 Play with the code.](https://codesandbox.io/s/why-react-hooks-withrepos-okjsj)__

This works and historically (along with [Render Props](https://tylermcginnis.com/react-render-props/)) has been the recommended solution for sharing non-visual logic. However, both these patterns have some downsides. 

First, if you're not familiar with them (and even when you are), your brain can get a little wonky following the logic. With our `withRepos` HOC, we have a function that takes the eventually rendered component as the first argument but returns a new class component which is where our logic lives. What a convoluted process.

Next, what if we had more than one HOC we were consuming. As you can imagine, it gets out of hand pretty quickly.

```js
export default withHover(
  withTheme(
    withAuth(
      withRepos(Profile)
    )
  )
)
```

Worse than ^ is what eventually gets rendered. HOCs (and similar patterns) force you to restructure and wrap your components. This can eventually lead to "wrapper hell" which again, makes it harder to follow.

```html
<WithHover>
  <WithTheme hovering={false}>
    <WithAuth hovering={false} theme='dark'>
      <WithRepos hovering={false} theme='dark' authed={true}>
        <Profile 
          id='JavaScript'
          loading={true} 
          repos={[]}
          authed={true}
          theme='dark'
          hovering={false}
        />
      </WithRepos>
    </WithAuth>
  <WithTheme>
</WithHover>
```

### Current State

So here's where we're at.

+ React is hella popular.
+ We use Classes for React components cause that's what made the most sense at the time.
+ Calling super(props) is annoying.
+ No one knows how "this" works.
+ OK, calm down. I know YOU know how "this" works, but it's an unnecessary hurdle for some.
+ Organizing our components by lifecycle methods forces us to sprinkle related logic throughout our components.
+ React has no good primitive for sharing non-visual logic.

Now we need a new component API that solves all of those problems while remaining __simple__, __composable__, __flexible__, and __extendable__. Quite the task, but somehow the React team pulled it off.

### React Hooks

Since React v0.14.0, we've had two ways to create components - classes or functions. The difference was that if our component had state or needed to utilize a lifecycle method, we had to use a class. Otherwise, if it just accepted props and rendered some UI, we could use a function. 

Now, what if this wasn't the case. What if instead of ever having to use a class, we could just always use a function. 

> Sometimes, the elegant implementation is just a function. Not a method. Not a class. Not a framework. Just a function. 
>
> \- John Carmack. Oculus VR CTO.

Sure we'd need to figure out a way to add the ability for functional components to have state and lifecycle methods, but assuming we did that, what benefits would we see? 

Well, we would no longer have to call `super(props)`, we'd no longer need to worry about `bind`ing our methods or the `this` keyword, and we'd no longer have a use for Class Fields. Essentially, all of the "superficial" issues we talked about earlier would go away. 

```js
(ノಥ,_｣ಥ)ノ彡 React.Component 🗑

function ヾ(Ő‿Ő✿)
```

Now, the harder issues. 

+ State
+ Lifecycle methods
+ Sharing non-visual logic

##### State

Since we're no longer using classes or `this`, we need a new way to add and manage state inside of our components. As of React v16.8.0, React gives us this new way via the `useState` method. 

> `useState` is the first of many "Hooks" you'll be seeing in this course. Let the rest of this post serve as a soft introduction. We'll be diving much deeper into `useState` as well as other Hooks in future sections.

`useState` takes in a single argument, the initial value for the state. What it returns is an array with the first item being the piece of state and the second item being a function to update that state.

```js
const loadingTuple = React.useState(true)
const loading = loadingTuple[0]
const setLoading = loadingTuple[1]

...

loading // true
setLoading(false)
loading // false
```

As you can see, grabbing each item in the array individually isn't the best developer experience. This is just to demonstrate how `useState` returns an array. Typically, you'd use Array Destructuring to grab the values in one line.

```js
// const loadingTuple = React.useState(true)
// const loading = loadingTuple[0]
// const setLoading = loadingTuple[1]

const [ loading, setLoading ] = React.useState(true) // 👌
```

Now let's update our `ReposGrid` component with our new found knowledge of the `useState` Hook.

```jsx
function ReposGrid ({ id }) {
  const [ repos, setRepos ] = React.useState([])
  const [ loading, setLoading ] = React.useState(true)

  if (loading === true) {
    return <Loading />
  }

  return (
    <ul>
      {repos.map(({ name, handle, stars, url }) => (
        <li key={name}>
          <ul>
            <li><a href={url}>{name}</a></li>
            <li>@{handle}</li>
            <li>{stars} stars</li>
          </ul>
        </li>
      ))}
    </ul>
  )
}
```

__[💻 Play with the code.](https://codesandbox.io/s/why-react-hooks-usestate-ddhc4)__

+ State ✅
+ Lifecycle methods 
+ Sharing non-visual logic

##### Lifecycle methods

Here's something that may make you sad (or happy?). When using React Hooks, I want you to take everything you know about the traditional React lifecycle methods as well as that way of thinking, and forget it. We've already seen the problem of thinking in terms of the lifecycle of a component - "This [lifecycle] divide naturally forces us to sprinkle related logic throughout the component." Instead, think in terms of __synchronization__. 

Think of any time you've ever used a lifecycle event. Whether it was to set the initial state of the component, fetch data, update the DOM, anything - the end goal was always synchronization. Typically, synchronizing something outside of React land (an API request, the DOM, etc.) with something inside of React land (component state) or vice versa.

When we think in terms of synchronization instead of lifecycle events, it allows us to group together related pieces of logic. To do this, React gives us another Hook called `useEffect`. 

Defined, `useEffect` lets you perform side effects in function components. It takes two arguments, a function, and an optional array. The function defines which side effects to run and the (optional) array defines when to "re-sync" (or re-run) the effect.

```js
React.useEffect(() => {
  document.title = `Hello, ${username}`
}, [username])
```

In the code above, the function passed to `useEffect` will run whenever `username` changes. Therefore, syncing the document's title with whatever `Hello, ${username}` resolves to.

Now, how can we use the `useEffect` Hook inside of our code to sync `repos` with our `fetchRepos` API request?

```jsx
function ReposGrid ({ id }) {
  const [ repos, setRepos ] = React.useState([])
  const [ loading, setLoading ] = React.useState(true)

  React.useEffect(() => {
    setLoading(true)

    fetchRepos(id)
      .then((repos) => {
        setRepos(repos)
        setLoading(false)
      })
  }, [id])

  if (loading === true) {
    return <Loading />
  }

  return (
    <ul>
      {repos.map(({ name, handle, stars, url }) => (
        <li key={name}>
          <ul>
            <li><a href={url}>{name}</a></li>
            <li>@{handle}</li>
            <li>{stars} stars</li>
          </ul>
        </li>
      ))}
    </ul>
  )
}
```

__[💻 Play with the code.](https://codesandbox.io/s/why-react-hooks-useeffect-0m35p)__

Pretty slick, right? We've successfully gotten rid of `React.Component`, `constructor`, `super`, `this` and more importantly, we no longer have our effect logic sprinkled (and duplicated) throughout the component.

+ State ✅
+ Lifecycle methods ✅
+ Sharing non-visual logic

##### Sharing non-visual logic

Earlier we mentioned that the reason React didn't have a great answer to sharing non-visual logic was because "React couples UI to a component". This lead to overcomplicated patterns like [Higher-order components](https://tylermcginnis.com/react-higher-order-components/) or [Render props](https://tylermcginnis.com/react-render-props/). As you can probably guess by now, Hooks have an answer for this too. However, it's probably not what you think. There's no built-in Hook for sharing non-visual logic, instead, you can create your own custom Hooks that are decoupled from any UI. 

We can see this in action by creating our own custom `useRepos` Hook. This Hook will take in an `id` of the Repos we want to fetch and (to stick to a similar API) will return an array with the first item being the `loading` state and the second item being the `repos` state. 

```js
function useRepos (id) {
  const [ repos, setRepos ] = React.useState([])
  const [ loading, setLoading ] = React.useState(true)

  React.useEffect(() => {
    setLoading(true)

    fetchRepos(id)
      .then((repos) => {
        setRepos(repos)
        setLoading(false)
      })
  }, [id])

  return [ loading, repos ]
}
```

What's nice is any logic that's related to fetching our `repos` can be abstracted inside of this custom Hook. Now, regardless of which component we're in and even though it's non-visual logic, whenever we need data regarding `repos`, we can consume our `useRepos` custom Hook.

```js
function ReposGrid ({ id }) {
  const [ loading, repos ] = useRepos(id)

  ...
}
```

```js
function Profile ({ user }) {
  const [ loading, repos ] = useRepos(user.id)

  ...
}
```

__[💻 Play with the code.](https://codesandbox.io/s/why-react-hooks-custom-hooks-t4rqv)__

+ State ✅
+ Lifecycle methods ✅
+ Sharing non-visual logic ✅

***

The marketing pitch for Hooks is that you're able to use state inside function components. In reality, Hooks are much more than that. They're about improved code reuse, composition, and better defaults. There's a lot more to Hooks we still need to cover, but now that you know WHY they exist, we have a solid foundation to build on.

***

This was originally published at [TylerMcGinnis.com](https://tylermcginnis.com/why-react-hooks/) and is part of our [React Hooks](https://tylermcginnis.com/courses/react-hooks/) course. If you enjoyed this post, check it out.