Published on

Converting React Class Component to Functional Hooks Component

Authors

Today I had to convert a class style React component to the functional hook based one. Initially it was a bit tricky but after converting one it became super easy for others. I really dig the functional approach as you can drop a lot of the frivalous cruft you have to usually add when working with classes for example with the functional approahc:

  • No need for this: There is no need to keep adding this.someFunction = this.someFunction.bind(this) to the constructor to make sure you are using the same this in the class method
    • Just be sure to define inner methods as arrow functions (const myMethod = () => {}) instead of normal functions (myMethod() {}) as this will ensure you use the same this as the wrapping component function.
  • Much less verbose: Far less verbose as you do not need to keep referring to this.prop.foo or this.state.bar instead you simply refer to them as foo and bar respectively.
  • Props all declared in one place: Props are more readable as you can see the destructured props in one place at the top of your functional component instead of having to find all this.props in your class to find all props.
    • You also do not need to keep destructuring this.props if you normally prefer destructuring them where you use them for readability.

Below I provide an example of a class style component and the equivalent functional version as well as a mini cheat sheet for how to convert from the class based approach to the functional approach.

Original Class Style React Component

import React, { Component } from 'react'
class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      name: '',
      surname: '',
    }
    this.someClassMethod = this.someClassMethod.bind(this)
  }

  componentWillMount() {
    //do foo
  }

  componentWillUnmount() {
    //do bar
  }

  someClassMethod() {
    //does something
    this.setState({ name: 'Bill' })
  }

  render() {
    return (
      <div>
        {' '}
        Hi {this.state.name} {this.state.surname} {this.props.emoji} {this.props.someOtherProp}{' '}
      </div>
    )
  }
}

export default MyComponent

React 16 Functional Hook Style Component

import React, { useEffect, useState } from 'react'
function MyComponent({ emoji, someOtherProp }) {
  const [name, setName] = useState('')
  const [surname, setSurname] = useState('')

  useEffect(() => {
    //do foo

    return () => {
      // do bar
    }
  }, [])

  const someClassMethod = () => {
    //does something
    setName('Bill')
  }

  return (
    <div>
      {' '}
      Hi {name} {surname} {emoji} {someOtherProp}{' '}
    </div>
  )
}

export default MyComponent

When using the useState hook to work with state you can refer to the previous state of something. For example for name this would be done as follows: setName(previousName => previousName + name);. In this example the new name is concatenated with the existing name.

Mini Cheatsheet

Based on my current understanding and experience moving an existing class based component to the functional hooks based approach my cheatsheet would look as follows:

React Class ComponentReact Functional Hooks Component
import React, { Component } from "react";import React, { useEffect, useState } from "react";
class MyComponent extends React.Component {function MyComponent({ emoji, someOtherProp }) {
constructor(props){Not needed - anything you need to initialize for later just put as lets or consts just after the { where you defined your component
this.state = { name: ""};const [name, setName] = useState("");
this.someClassMethod = this.someClassMethod.bind(this);No need, this is the same as long as you use arrow functions (=>) to define your methods inside the functional component
componentWillMount() { /*foo*/}useEffect(() => {/*foo*/},[]) note the [] at the end which are needed to make this run once
componentWillUnmount() { /*bar*/}useEffect(() => { return () => {/*bar*/}},[]) note the [] at the end which are needed to make this run once
componentWillMount() { /*foo*/} ... componentWillUnmount(){ /*bar*/}useEffect(() => {/*foo*/ return () => {/*bar*/}},[]) note the [] at the end which are needed to make this run once
someClassMethod() {const someClassMethod = () => {
this.setState({name: 'Bill'});setName('bill') where you defined const [name, setName]= useState("") higher up
render(){ return (<div> Hello </div>);return (<div> Hello </div>);
this.somepropfunction yourComponent({someprop}) { ... someprop You simply refer to prop that was destructured in your functional component definition