{ The Rithm Blog. }

React Hooks! - An Introduction October 22, 2019

Hooks are here! At Rithm School we are constantly reviewing our curriculum and making sure that we are up to date on the latest technologies. When we notice a new technology or technique rising in popularity (and also becoming a common question that interviewers ask), we have the ability to quickly incorporate it into what we are teaching.

Since the introduction of hooks into React, we have clearly seen a strong interest in the community to use hooks in new React projects. My goal is to give you a brief introduction to hooks and show you how they can be used instead of using class components with setState.

Hooks - Motivation

React added hooks in version 16.8 to fix three common issues in React code. The motivation for the new feature was very well described by Sophie Alpert and Dan Abramov in this React Conf 2018 talk. The three main issues are:

  1. How to effectively reuse react logic without having to make lots of wrapper components (i.e. wrapper hell)
  2. Logic in the component can be spread across many different lifecycle methods, creating lots of extra complicated code
  3. Before React 16.8, in order to have stateful components, you needed to create a class which requires a lot of code and an in depth understanding of the keyword “this”.

I should admit, I was pretty skeptical of these issues when I first looked into hooks. It felt to me like these problems were not so bad, and that the real driver behind adding hooks was mainly to make react seem more like a functional programming framework and less class based (the React community seems to like functional programming).

After some time coding with hooks, I have definitely changed my mind. I think the reduction in boilerplate code that comes with hooks is very liberating and very easy to follow once you get the hang of it.

I'd like to jump into some discussion on hooks as soon as possible, but first, let's talk about a few important topics!

Array Destructuring

There is one quick JavaScript feature we need to understand before we can get into hooks. At times, you might have an array and the element at each position is a value type (For example, the first element in the array is always an id and the second element is always a name). If we want to get each value out of the array and give it a variable name, this used to be the way to do it in JavaScript:

// index 0 is an id, index 1 is a name
const arr = [9999, "Tim"];

// old way to get values:
const id = arr[0];
const name = arr[1];

This solution works just fine, but it's verbose and we have a much more concise way of doing this with array destructuring. With array destructuring we could instead create two variables at once that correspond to values in each position in the array:

// index 0 is an id, index 1 is a name
const arr = [9999, "Tim"];

// old way to get values:
// const id = arr[0];
// const name = arr[1];

// es6 array destructuring, equivalent to commented out lines
const [id, name] = arr;

The commented out code above is equivalent to the array destructuring example on the last line of code. Array destructuring is a useful feature that makes our code less verbose and it is used frequently in React (especially with hooks).

Function Components vs Class Components

So when do you use a hook and what is it? Well, a hook is a feature that gives function components more abilities. The name, hook, was created because they are new functions that hook into react features.

But wait, what's a function component? Let's make sure we have that down before we move on. In react, we have two options for making a component. Let's use a simple example:

Class Component

class Title extends React.Component {
  render() {
    return <h1>{this.props.title}</h1>;
  }
}

Function Component

function Title(props) {
  return <h1>{prop.title}</h1>;
}

As you can see, a function component is simply a function that receives the props for the component as a parameter and the function's only job is to implement a render method. These two examples achieve the same goal, but what if we wanted to add some stateful feature? For example, what if the font size of the title would increase on click?

To add the feature in the class version, we would have to:

  1. Create a constructor
  2. Define initial state in the constructor
  3. Add a method that calls setState on click
  4. Bind the new click handler function in the constructor to this

Before React hooks, the function component had no way of supporting this feature. So when are hooks used? The are used within function components. They allow function components the ability to hook into React features that they did no have access to before (like state).

useState example

Let's look at implementing the title clicking example from the last section. First, let's do it with class components:

class Title extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      fontSize: "20px"
    };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(prevState => {
      const fontSize = `${parseInt(prevState.fontSize) + 5}px`;
      return {fontSize};
    });
  }

  render() {
    return (
      <h1 style={this.state} onClick={this.handleClick}>
        {this.props.text}
      </h1>
    );
  }
}

To summarize, we need a constructor that creates the initial state (in this case, a font size of 20px), we have handleClick function that needs to bind to the keyword this and the implementation that uses a setState updater function. Finally, in render, we call the handleClick method during a onClick event.

Now let's take a look at this code using hooks. Remember, hooks are always used with function components:

import React, {useState} from 'react';

function Title(props) {
  const [fontSize, updateFontSize] = useState("20px");

  function handleClick()  {
    updateFontSize(fontSize => `${parseInt(fontSize) + 5}px`);
  }

  return (
    <h1 style={{fontSize}} onClick={handleClick}>
      {props.text}
    </h1>
  );
}

There is a lot to go over here, but first, just take a second to appreciate how much less code there is. We have gone from a class with 3 methods to a smaller function component that does the same thing.

Ok, now let's get into the details. Notice that we are now importing useState from react. useState is a hook. It allows our functional component to hook into the state feature of React. On the first line of our function component, we have a call to useState. The parameter to the function is the value that we want for initial state. The useState function returns an array with 2 elements. The first element in the array is the current value of state (in this case the fontSize) and the second element is a function that we can use to update the fontSize.

The update function we got back is similar to setState but it can only be used to update the fontSize, not other values from the state. Also, the update function, updateFontSize in this case, always replaces the value of state completely, it does not do a merge (like setState does).

Next, we have a handleClick function, just like before, but now there is no need to deal with binding in the constructor. In our handleClick, we simply call the updateFontSize function that we got from useState. updateFontSize accepts a function as a parameter when the state that we want to update depends on the previous value of the state. In the case in which the state is independent, we can just set it directly: updateFontSize("50px");.

Now, when happens when the user clicks on the text? Well, the handleClick method gets invoked and the updateFontSize method is called with our new font size. The updateFontSize invocation will trigger a re-render. During a re-render, useState will be invoked again, but React remembers that fact that the state has already been initialized, so it will not be initialized again. Instead, this time, React will return the latest value of the state (in this case the new fontSize that is 5 pixels larger). With the updated fontSize, the function will return the JSX for the title with larger text.

Multiple Calls to useState

The example we've seen so far is a simple component. Let's say we want to have more state about our title. Perhaps we also want to randomly change the color of the title whenever the user clicks. We could make our state value an object and then have 2 keys: fontSize and color. However, the common pattern is to call useState more than once:

function Title(props) {
  const [fontSize, changeFontSize] = useState("20px");
  const [color, changeColor] = useState("black");

  function handleClick()  {
    changeFontSize(fontSize => `${parseInt(fontSize) + 5}px`);
    const colors = ["red", "green", "blue", "black"];
    const index = Math.floor(Math.random() * colors.length);
    changeColor(colors[index]);
  }

  return (
    <h1 style={{fontSize, color}} onClick={handleClick}>
      {props.text}
    </h1>
  );
}

Some important notes about this example, the order of the useState calls matters. The first useState call is always going to return the value for font and the second call is always going to return the value for the color. So you never want to make your useState calls conditional. Additionally, you do not want to invoke useState within a loop. The calls to useState should be at the top level of the function.

This is only the tip of the iceberg for hooks in react. There are other built in hooks to explore, like useEffect and you can even make your own custom hooks. We will discuss more about hooks in a future blog post, but I hope this is enough info to get started.

Written by Tim Tim

Back to all posts

Get Started with Rithm School