Migrating from class components to React hooks
Tags: react, reactnative, webdev
Published: 2020-01-11 17:48:37

Since the release of [v16.8](https://reactjs.org/blog/2019/02/06/react-v16.8.0.html), **hooks** have been introduced to React. You may have already heard of hooks as a way to use common React features without writing a class-based component.

Hooks provide an interface for creating powerful functional components. They can be used to introduce state and manage side-effects. _Note that this was previously not possible in functional components._

Prior to hooks, we relied on class-based components to use state and manage side-effects by using [lifecycle methods](https://reactjs.org/docs/state-and-lifecycle.html). If you have used React, you have most likely written or encountered a class-based component.

Class-based components are still to be supported by React. But, you may want to know how to transform them into functional components using hooks. _This guide will teach you how to do just that!_

### Class-based component (before)

Below is a typical example of a class-based component. It manages some internal state and uses lifecycle methods. 

_`Counter` displays a count which can be incremented and decremented. Updating the name in the text field will also be reflected in the document title._ 

```javascript
import React, { Component } from "react";

class Counter extends Component {
  constructor(props) {
    super(props);

    this.state = {
      name: 'Joe',
      count: 0
    }

    this.updateName = this.updateName.bind(this)
    this.incrementCount = this.incrementCount.bind(this)
    this.decrementCount = this.decrementCount.bind(this)
  }

  componentDidMount() {
    document.title = `${this.state.name}'s counter`
  }

  componentDidUpdate(prevProps, prevState) {
    if(prevState.name !== this.state.name) {
        document.title = `${this.state.name}'s counter`
    }
  }

  incrementCount() {
    this.setState(state => {
      return { count: state.count + 1 }
    });
  }

  decrementCount() {
    this.setState(state => {
      return { count: state.count - 1 }
    })
  }

  updateName(e) {
    this.setState({ name: e.target.value })
  }

  render() {
    return (
        <div>  
            <label>
                Name:&nbsp;
                <input 
                    type='text' 
                    name='name' 
                    placeholder='Insert name'
                    defaultValue={this.state.name} 
                    onChange={this.updateName} 
                />
            </label>

            <br/>

            <button onClick={this.decrementCount}>-1</button>
            &nbsp;
            <span>{this.state.count}</span>
            &nbsp;
            <button onClick={this.incrementCount}>+1</button>
        </div>
    );
  }
}
```

Before we continue, lets dissect the component to understand how it works. 

Firstly, we declare an initial state in the constructor for `count` and `name`. We also declare functions to update these states: `decrementCount`, `incrementCount` and `updateName`. To be able to invoke these functions with DOM events, [we must explicitly bind them](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind)  with `this`, as shown in the constructor.

Secondly, we introduce two lifecycle methods - `componentDidMount` and `componentDidUpdate`. The former is to set the initial document title when the component mounts. The latter is to update the document title on subsequent re-renders when `name` changes.

Finally, the `render` function returns [JSX](https://reactjs.org/docs/introducing-jsx.html) to declare markup including interactions between DOM elements and component state.

### Functional component using hooks (after)

Now it's time to see an equivalent as a functional component using hooks.

```javascript
import React, { useState, useEffect } from 'react';

const Counter = () => {
    const [name, setName] = useState('Joe')
    const [count, setCount] = useState(0)

    useEffect(() => {
        document.title = `${name}'s counter`
    }, [name])

    return (
        <div>  
          <label>
            Name:&nbsp;
            <input 
                type='text' 
                name='name' 
                placeholder='Insert name'
                defaultValue={name} 
                onChange={e => setName(e.target.value)} 
            />
          </label>

          <br/>

          <button onClick={() => setCount( count - 1 )}>-1</button>
          &nbsp;
          <span>{count}</span>
          &nbsp;
          <button onClick={() => setCount( count + 1 )}>+1</button>
        </div>
    )
}
```

As shown above, there is quite a lot of difference in the component after migrating to hooks. You may notice that the structure of the component has changed and it is relatively easier to interpret what is going on. There are also considerably less lines of code to achieve the same functionality as the class-based component. 

As we did earlier, let's dissect this component too.

From `React`, we import two hooks: `useState` and `useEffect`  

#### What are useState and useEffect?

`useState` is used to declare state in your functional component. You must invoke it with an initial value and it will return an array of two values - _current state and a function to update it_.

`useEffect` is used to manage side-effects in your functional component. It receives a function as a parameter which will handle an action to be invoked after the component renders. It is possible to declare multiple effects in a component. This helps to maintain separate concerns for each side-effect, rather than creating conditional logic in one function. 

#### How are they used in this component?

In the new `Counter` component, we apply the `useState` hook to declare two state variables: `name` and `count`. Both have respective functions  to update them: `setName` and `setCount`. 

We call `useEffect` to change the document title post-render. A dependency array is supplied as the second argument to ensure that the side-effect is only triggered when `name` changes.

Similarly to the class-based component, we use JSX to declare markup and binds events to DOM elements. Notice how we can pass the functions provided by `useState` directly to our event handlers. This avoids us having to manually define functions to set our state. 

### Summary

There we have it! We have successfully migrated a class-based component to a functional component using hooks. It is worth noting that most features of class-based components can now be achieved using functional components thanks to hooks. You can also create your own custom hooks, but we'll save that for another day.

**Dont worry! This does not mean you must migrate all of your existing code to use hooks, but it is something to consider when you are building new ones.**

Read more about hooks [here](https://reactjs.org/docs/hooks-overview.html)!

Here are Codepen links for the snippets:  
*[Counter as a class](https://codepen.io/zubhav/pen/GRKaVJg)*  
*[Counter as a function with hooks](https://codepen.io/zubhav/pen/vYBwopN)*