Weekend JavaScript Newsletter

Converting A React Component To Hooks

> 9 months ago

For a long time if we wanted to create a stateful component in React we needed to create a class and inherit it from React.Component. But dealing with classes can be a frustrating experience and contradicts the functional nature of React itself. I wrote a rant article on that before.

With the release of React v16.7.0-alpha, React team proposed a way to create stateful components based on functions, not on classes.

Enter the hooks.

Hooks API is a set of functions that allows you to “hook into” the lifecycle or state right from your component function.

In order to use them you need to update to the bleeding age: npm i react@next react-dom@next --save.

Now let’s migrate a class component to a functional one. Imaging, we have a component like this.

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      loading: false,
    }
  }

  async function componentWillMount({ setState }) {
    setState({ loading: true })
    const employee = await fetchEmployee(this.props.employeeId)
    setState({ employee, loading: false })
  }

  async remove() {
    await removeEmployee(this.state.employee.id)
    this.setState({ employee: undefined })
  }

  render() {
    const { loading, employee } = this.state
    return (
      <div>
        {
          loading && <Spinner />
        }
        {
          !loading && (
            <div>
              <span>{ employee.name }</span>
              <button onClick={this.remove}>Remove</button>
            </div>
          )
        }
      </div>
    )
  }
}

MyComponent shows a single employee. It needs to fetch it via some sort of API first. That’s why we are using componentWillMount. And it also can remove the employee by using another asynchronous API call. When the data is loading, it displays a spinner.

Now, how should we proceed with the hooks?

import { useState, useEffect } from 'react'

function MyComponent({ employeeId }) {
  const [loading, setLoading] = useState(false)
  const [employee, setEmployee] = useState()

  useEffect(async () => {
    setLoading(true)
    const employee = await fetchEmployee(this.props.employee_id)
    setLoading(false)
    setEmployee(employee)
  })

  return (
    <div>
      {loading && <Spinner />}
      {!loading && (
        <div>
          <span>{employee.name}</span>
          <button onClick={remove}>Remove</button>
        </div>
      )}
    </div>
  )

  async function remove() {
    await removeEmployee(employee.id)
    setEmployee(undefined)
  }
}

As you can see code became a little bit more straightforward. this is gone. Let’s see what happened.

So we used two hooks here. The first one is useState. As the name implies it allows you to hook into state functionality. It accepts a default value and returns the current value and a setter. So instead of calling this.setState, we are now gonna call this setter directly when we want to update the value.

The initialization logic is moved from componentWillMount to useEffect. This is a special hook that is here to allow hooking into the lifecycle of the component.

Also note, how remove function is now defined as a simple function inside of our component function. Because of that, it is able to access the same scope variables as the render part, for example employee. This is a simple JavaScript, no magic here.

Conclusion

I hope you are as excited as I am about the hooks. This is a long-awaited change that will help to clean up many class-based components (many of which by now looks not better than the infamous jQuery spaghetti).

By the time I’m writing this, the API is still in alpha so some changes may follow. But it is clear that React is headed to a brighter no-OOP future, and I’m really glad it is.

Weekend JavaScript

Get all the best JavaScript articles in your inbox every Friday. Plus occassional articles like this one. It's Free!
1008 have already joined.