Introduction to React Hooks

joshtronic

Function components are great, but they are freakin’ dumb. They do exactly what they are told by way of properties and that’s about it. The moment you need them to be mindful of their state, you are forced to rewrite them as class components. Fortunately, times are a changin’ in React-land as a lot of class component-only features are making their way to function components.

One such feature, which is currently in alpha and is expected to land in React v16.7, is Hooks. Hooks brings statefulness and lifecycle methods, previously only available to class components to function components.

Getting Started

As mentioned, this feature is currently in alpha but is expected to be a part of the next point release of React. Because of this, we will need to specify a version when adding React to our project.

# Via npm
$ npm install --save react@next react-dom@next

# Or via Yarn
$ yarn add react@next react-dom@next

What Makes a Hook?

  • Must start with the word use.
  • Hooks can only be called from within function components and custom Hooks. This has the added bonus of keeping your component logic grouped together nicely.
  • Only use Hooks at the top level, never from inside of conditionals, loops or nested functions within your function component. This is to help ensure the Hooks are called in the same order with each render.

Component State

To create a stateful function component using Hooks, we will need to initialize the state using useState from the React package.

This method accepts a parameter to set the initial state, and returns an array containing the current state and a function to set the state.

Here’s a stateful function component that keeps track of a color that is set at random every time the button is pressed:

import React, { useState } from "react";
import { render } from "react-dom";

function StatefulFn() {
  const [color, setColor] = useState(false);

  function onClick() {
    const colors = [
      "#008F68",
      "#6DB65B",
      "#4AAE9B",
      "#FAE042",
      "#EFBB35",
      "#DFA612"
    ];

    setColor(colors[Math.floor(Math.random() * colors.length)]);
  }

  return (
    <button onClick={onClick} style={{ backgroundColor: color }}>
      Click to Change Button Color
    </button>
  );
}

const container = document.createElement("div");
document.body.appendChild(container);
render(<StatefulFn />, container);

The useState method is very much intended to be used with a single value, and not a state object the way class components work. Because of this, we either have to maintain a state object manually ourselves, or more simply, just call useState more than once to keep track of each variable in our state:

const [color, setColor] = useState(false);
const [size, setSize] = useState('medium');
const [reptile, setReptile] = useState('alligator');

Component Lifecycle

Another gotcha of using function components has been their lack of lifecycle methods. Another part of the Hooks feature is the addition of useEffect which is a combination of componentDidMount, componentDidUpdate and componentWillUnmount.

In more simple terms, useEffect will fire after the initial render and any subsequent re-renders.

If you’re familiar with the React class component lifecycle, you know that it’s bad to put code with side effects in your render method. That’s why the lifecycle methods exist to begin with.

One such side effect is firing off a network request, and then updating the state with a value that’s returned. In the next example, I’m going to simulate a very slow network connection by using setTimeout and will display a loading message while we wait:

import React, { useEffect, useState } from "react";
import { render } from "react-dom";

function EffectedFn() {
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    setTimeout(() => {
      setLoading(false);
    }, 1000 * 10);
  });

  return (
    <div>
      {loading && <span>Loading...</span>}
      {!loading && <span>All Done!</span>}
    </div>
  );
}

const container = document.createElement("div");
document.body.appendChild(container);
render(<EffectedFn />, container);

Something cool about useEffect is that it doesn’t block the browser from updating the screen the way componentDidMount and componentDidUpdate do. This helps to ensure that things seem responsive and snappy to your users!

To help improve performance even further, especially when working with multiple state variables, you can tell useEffect to only fire when a certain value has changed:

useEffect(() => {
  setTimeout(() => {
    setLoading(false);
  }, 1000 * 10);
}, [loading]);

But I don’t wanna use Hooks!

What’s really great about Hooks is you don’t have to use them if you don’t want to. If you’re keen on using classes, you can keep on truckin’. If you’re scared that you will need to update all of your existing, non-hooked functions, don’t be.

Hooks are 100% backwards compatible so there’s no need to scramble to update any existing code. Classes aren’t going anywhere and if/when you’re ready to start using Hooks, as long as you’re on a version of React that supports it, you can do so.

Conclusion

Personally, I’m sick and tired of porting functions to be classes when I need state and lifecycle support so for me, Hooks are a very welcome addition.

Plus, it’s super easy to create your own hooks or use hooks developed by the greater React community. There are already collections of available hooks that are popping up left and right.

If you’re like me and ready to start using Hooks today and want to see the code from this article in action, head over to CodeSandbox.

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#