Convert Your Old Class Components to Functions. Just Do It Already!
Tags: react, javascript
Published: 2021-01-23 02:41:51

It's now generally acknowledged that using hooks in React is better than using classes.  There are a ton of blogs and discussions on the subject and over time they have inched more and more uniformly toward hooks.  Yet my React training still used classes, and I know that many developers still use classes today as well.  My guess is a lot of them do so for the same reason I've been sticking to classes: I'm already familiar with class-based stateful components, and I'd have to re-learn how to implement state and lifecycles if I make the switch.  Maybe that's not the greatest reason, but with so much out there that needs to be learned, it is hard to prioritize learning a new way of doing something when the "old way" is already working perfectly for me today.

But here's the thing - in React interviews, they expect you to use hooks.  If you use a class you get the toughest interview question of them all..."Why did you do it that way?" :fearful:...:cold_sweat:...:cry:

Luckily, after a *ahem* learning experience finally convinced me it's time to learn about hooks, I found out they're really not bad at all, at least in the simplest implementations.  

There are two "main" hooks and that was all I am initially concerned with: `useState` and `useEffect`.  So below, I'm going to create an unstyled digital clock component, using both classes and hooks, to show off how to use both of these.  At the end I'll do a side-by-side comparison.

### Class

Before we can even get started, we need to set up the component.  first, import React, and its Component class, and then create our Clock component that inherits from it.
```
import React, {Component} from 'react';

export default class Clock extends Component {};
```

Then, let's start by setting up our state.  We need to create a Date Object representing the current date/time and set it in the component's state with a key of `currentTime`.
```
  state = {
    currentTime: new Date
  };
```

And then we can call our render function to display that value in the DOM.  To convert it to a time string, we'll use `toLocaleTimeString()`.
```
import React, {Component} from 'react';

export default class Clock extends Component {
  state = {
    currentTime: new Date
  };

  render() {
    return(
      <h2>{this.state.currentTime.toLocaleTimeString()}</h2>
    );
  };
};
```

And that will display the time on the page.  But to make it a clock, we need it to "tick" each second as time goes by.  We start by defining a `tick()` function that sets the state to the new moment in time.  Then we want to call that tick function every second by setting up a one second interval.  For the interval, we need to wait until the component is mounted, then start the interval timer.  To do something "once the component is mounted" we use the `componentDidMount` lifecycle method.  Finally, if and when the Clock component is unmounted, we'd want the interval to stop so that the computer isn't constantly counting for no reason.  To do something "once the component is unmounted" we use `componentWillUnmount` which runs just before the component is destroyed.
```
import React, {Component} from 'react';

export default class Clock extends Component {
  state = {
    currentTime: new Date
  };

  tick() {
    this.setState({currentTime: new Date});
  };

  componentDidMount() {
    this.int = setInterval(() => this.tick(), 1000);
  };

  componentWillUnmount() {
    clearInterval(this.int);
  };

  render() {
    return(
      <h2>{this.state.currentTime.toLocaleTimeString()}</h2>
    );
  };
};
```

And now we have ourselves a ticking clock!


### Hooks

Now, let's see how to do the exact same thing using hooks.  Again, we need to start by setting up the component.  Notice we need to move the export statement to the bottom now.
```
import React, {useState, useEffect} from 'react';

const Clock = () => {};

export default Clock;
```

Then, once again, we'll set up our state with the same key & value.  Here, we're defining two separate variables at the same time.  `currentTime` is our key, and `setCurrentTime` equates to calling `this.setState()` in a class.  Finally, calling `useState` actually calls `setCurrentTime`, so you need to pass an argument to set up the initial state, in this case a Date object.
```
const [currentTime, setCurrentTime] = useState(new Date);
```

What we render will remain unchanged, but since we're using a functional, not class, component we just need to return the JSX, we don't need to use the `render()` function.
```
import React, {useState, useEffect} from 'react';

const Clock = () => {
  const [currentTime, setCurrentTime] = useState(new Date);

  return(
    <h2>{this.state.currentTime.toLocaleTimeString()}</h2>
  );
};

export default Clock;
```

And now it's time to get that clock a-ticking.  We'll start by defining that `tick()` function again which sets the state to the new moment.  We do this by defining a normal function (*there's no class for instance methods*) which uses our new `setCurrentTime` function variable to change state.  Where things get interesting is, since there's no lifecycle methods without the class, we need to use `useEffect()`.  This function actually includes both the `componentDidMount` and `componentWillUnmount` methods all in one.  We still need to define our interval and set the callback to `tick()`, but now we will have our `useEffect` function return *another function*. This returned function stands in for `componentWillUnmount` and should be used to clean up any services that were started once the component is destroyed.
```
import React, {useState, useEffect} from 'react';

const Clock = () => {
  const [currentTime, setCurrentTime] = useState(new Date);

  function tick() {
    setCurrentTime(new Date);
  };

  useEffect(() => {
    let int = setInterval(() => tick(), 1000);
    return cleanup => {
      clearInterval(int);
    };
  });

  return(
    <h2>{currentTime.toLocaleTimeString()}</h2>
  );
};

export default Clock;
```

Lastly, we can take this one step further by converting our functions to arrow functions.  Take a look at the side-by-side below to see the refactored component.

### Comparison
| Hooks | Class |
|:-----:|:-----:|
| ![Function-Based Clock Component](https://dev-to-uploads.s3.amazonaws.com/i/0dhbnxx1q5cvlit8j30u.png) | ![Class-Based Clock Component](https://dev-to-uploads.s3.amazonaws.com/i/hn720p5u72q2zad3e1n3.png) |

The functional way looks a lot more concise doesn't it?  So what do you think...are you convinced to start using hooks yet, or do you need to learn the hard way like I did?

#### Since I like to have defined rules so that I can just follow down the list, here is your Converting Class To Function Checklist:

1. Change the import statement
  * From: `import React, {Component} from 'react'`
  * To: `import React, {useState, useEffect} from 'react'`
2. Change the component declaration
  * From: `export default class Clock extends Component {}`
  * To: `const Clock = () => {` & move the export to the end of the file
3. Change the state definition
  * From: `state = {currentTime: new Date};`
  * To: `const [currentTime, setCurrentTime] = useState(new Date);`
4. Drop `this.state.` from anywhere that uses state data
5. Change any `this.setState()` to the new function defined in `useState`
6. Change any instance methods/variables to regular functions/variables
  * From: `tick() {}` / `this.int =`
  * To: `function tick() {};` / `int =`
  * *Alt: convert function to arrow function `tick = () => {}`*
7. Finally, change any lifecycle methods to `useEffect()`
  * From: `componentDidMount() {};` / `componentWillUnmount() {}`
  * To: `useEffect()` which returns a cleanup function