Documentation

About

What is React Hookify?

React Hookify is a command line tool that converts React class components into functional components with Hooks! It will not alter your current code; instead a new 'hookified' file is created. This tool provides a convenient way to update outdated code, or to help learn how Hooks work. Feel free to try it out with our Demo!


Why Use Hooks?

In version 16.8, React introduced Hooks. Hooks provide a way to use stateful logic within a functional component. Simply put, they vastly reduce the need to use class components with React. Hooks address many of issues that developers have struggled with when using react:

  • It is currently very difficult to re-use stateful logic within components. The only solutions before hooks were to utilize higher order functions or to render props down to many, many components
  • Lifecycle methods force unrelated logic to be combined into a single method. This causes classes to be harder to understand and harder to test
  • Classes and 'this' are confusing and can be a barrier to learning react
While there are no plans to remove classes from React, hooks give React developers new tools to build their apps. Read more about hooks here.

Installation

Current Version: 1.0.13

Install with npm:

npm install -g react-hookify

To update to a more recent version (if previously installed):

npm update -g react-hookify

Usage

React Hookify is a CLI tool. It will not alter your current file; instead a new 'hookified' file will be created next to the file you run it on.

Run the command 'hookify' on the file path name:
hookify client/app.js
If file is in your current directory:
hookify app.js
Can accept multiple files at once:
hookify app1.js app2.js

Examples


Class Component to Functional Component

React Hookify is able to take a class component, and construct it in the form of a functional component. In this simple example, note the changes to the import statement, class declaration, and render method.

Input:
import React, { Component } from "react"

class App extends Component {
  render() {
    return <div>Hello</div>
  }
}
Output:
import React from "react"

function App() {
  return <div>Hello</div>
}

State to useState

In this example, stateful logic from the class component can be implemented with the useState hook. Destructuring the output from useState gives access to the state variable (firstName) and a function that can set it (setFirstName).

Input:
import React, { Component } from "react"

class App extends Component {
  constructor() {
    this.state = {
      firstName: "Bob",
      lastName: "Smith"
    }
  }

  render() {
    return <div>Hello {this.state.firstName} {this.state.lastName}</div>
  }
}
Output:
import React from "react"

function App() {
  const [firstName, setFirstName] = useState("Bob")
  const [lastName, setLastName] = useState("Smith")

  return <div>Hello {firstName} {lastName}</div>
}

setState

The example below shows how to set state with the useState hook. useState returns an array with the following structure: [variableName, setVariableName]. The second element can be used to set state.

Input:
import React, { Component } from "react"

class App extends Component {
  constructor() {
    this.state = {
      firstName: "Bob",
      lastName: "Smith"
    }
  }

  render() {
    return (
      <button
        onClick={() =>
          (this.setState = {
            firstName: "Bob",
            lastName: "Smith"
          })}
      >
        Click Here!
      </button>
    )
  }
}
Output:
import React from "react"

function App() {
  const [firstName, setFirstName] = useState("Bob")
  const [lastName, setLastName] = useState("Smith")

  return (
    <button
      onClick={() => {
        setFirstName("Tom")
        setLastName("Hanks")
      }}
    >
      Click Here!
    </button>
  )
}

Lifecycle Method to useEffect

The example below shows how lifecycle methods like componentDidMount, componentDidUpdate, and componentWillUnmount are translated into useEffect. While there is significant overlap in the functionality of lifecycles and useEffect, useEffect is not built to exactly mirror lifecycles. useEffect centers around the side effects caused to the functional component, not the timing of lifecycle calls. Please see the limitations section for more information.

Input:
 componentDidMount() {
    console.log('Mounted');
  }

  componentDidUpdate(prevProps, prevState) {
    console.log('Updated');
  }

  componentWillUnmount() {
    console.log('Unmounted');
  }
Output:
 //componentDidMount
  useEffect(() => {
    console.log("Mounted")
  }, [])

  //componentDidUpdate
  useEffect(() => {
    console.log("Updated")
  })

  //componentWillUnmount
  useEffect(() => {
    return () => {
      console.log("Unmounted")
    }
  }, [])

useEffect can also conditionally update your component based on a props or state variable. The example below shows how this conditional logic from componentDidUpdate can be converted into a useEffect using useEffect's second argument.

Input:
 componentDidUpdate(prevProps, prevState) {
    if (prevProps.counter !== this.state.counter) {
      console.log(this.state.counter);
    }
  }
Output:
 useEffect(() => {
    console.log(counter)
  }, [props.counter])

Lifecycle methods often force you to split the same logic into multiple methods. If you want a statement to run on componentDidMount and componentDidUpdate, the same statement must be written in both methods. With useEffect, this logic can be combined.

Hookify will find similiar lines of code in componentDidMount and componentDidUpdate and translate them into the same useEffect.

Input:
 componentDidMount() {
    console.log('Mounted');
    console.log(counter);
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevProps.counter !== this.state.counter) {
      console.log(counter);
    }
  }
Output:
 useEffect(() => {
    console.log("Mounted")
  }, [])

  useEffect(() => {
    console.log(counter)
  }, [counter])

Limitations


React-Hookify is not designed to handle 100% of cases. Some limitations include:

  • Any comments in a class component file will be removed in the new 'hookified' file.
  • React Hookify does not currently support 'get', 'set', and 'static' keywords.
  • RReact Hookify cannot currently translate any JSX with unclosed parentheses. So no smiley/frowney faces :) or :( sorry!
  • React Hookify does not support replacing the whole state with a new object or using the whole state at once. There is not an exact hook equivalent. For example, replacing the whole state using this.setState(newObject) will not be able to be translated.
  • React Hookify will not be able to translate instances where variable names do not actually exist on the current file. The package is built with parsing logic that looks for variable patterns. This may come up when using controlled forms.
    • If you try to implement a controlled form, but the actual form component is in a different file, React Hookify would not be able to identify the needed form names and values.
  • Lifecycle methods do not always map 1 to 1 with React Hooks. The article below (written by a React developer) highlights many of the differences. Sometimes it is necessary to rewrite code. Currently, the only lifecycle methods that React Hookify supports are componentDidMount, componentDidUpdate, and componentWillUnmount.