7 min read

React 16 added waves of new features, improving the way we build web applications. The most impactful update is the new Hooks feature in version 16.8. Hooks allow us to write functional React components that manage state and side effects, making our code cleaner and providing the ability to easily to share functionality. React is not removing class components, but they cause many problems and are a detriment to upcoming code optimizations.

The vision for Hooks is that all new components will be written using the API, resulting in more scalable web applications with better code. This tutorial will walk you through Hooks step-by-step and teach the core hook functionality by building a counter app.

An overview of hooks

Hooks provide the ability to manage state and side effects in functional components while also providing a simple interface to control the component lifecycle. The 4 built-in hooks provided by React are useState, useEffect, useReducer, and useContext.

  • useState replaces the need for this.state used in class components
  • useEffect manages side effects of the app by controlling the componentDidMount, componentDidUpdate, and componentWillUnmount lifecycle methods.
  • useContext allows us to subscribe to the React context
  • useReducer is similar to useState but allows for more complex state updates.

The two main hook functions that you will use are, useState and useEffect, which manage the standard React state and lifecycle. useReducer is used to manage more complex state and useContext is a hook to pass values from the global React context to a component. With the core specification updating frequently, it’s essential to find tutorials to learn React.

You can also build your own custom hooks, which can contain the primitive hooks exposed by React. You are able to extract component state into reusable functions that can be accessed by any component. Higher-order components and render props have traditionally been the way to share functionality, but these methods can lead to a bloated component tree with a confusing glob of nested React elements. Hooks offer a straightforward way to DRY out your code by simply importing the custom hook function into your component.

Building counter with hooks

To build our counter, we will use Create React App to bootstrap the application. You can install the package globally or use npx from the command line:

npx create-react-app react-hooks-counter
cd react-hooks-counter

React Hooks is a brand new feature, so ensure you have v16.8.x installed. Inside your package.json, the version of react and react-dom should look similar to the code snippet below. If not, update them and reinstall using the yarn command.

The foundation of hooks is that they are utilized inside functional components. To start, let’s convert the boilerplate file inside src/App.js to a functional component and remove the content.

At the top of the file, we can import useState and useEffect from React.

import React, { useState, useEffect } from 'react';

The most straightforward hook is useState since its purpose is to maintain a single value, so let’s begin there. The function takes an initial value and returns an array of arguments, with the item at the 0 index containing the state value, and the item at the 1 index containing a function to update the value. We will initialize our count to 0 and name the return variables count and setCount.

const [count, setCount] = useState(0);

NOTE: The returned value of the useState is an array. To simplify the syntax, we use array destructuring to extract the elements at the 0 and 1 index.

Inside our rendered React component, we will display the count and provide a button to increment the count by 1 by using setCount.

With a single function, we have eliminated the need to have a class component along with this.state and this.setState to manage our data. Every time you click the increment button, the count will increase by 1. Since we are using a hook, React recognizes this change in state and will re-render the DOM with this updated value.

To demonstrate the extensibility of the state updates, we will add buttons increment the count by 2, 5, and 10 as well. We will also DRY out our code by storing these values in an array. We iterate over this array using the .map() function, which will return an array of React components. React will treat this as sibling elements in the DOM. You are now able to increment the count by different values.

Now we will integrate the useEffect hook. This hook enables you to manage side effects and handle asynchronous events. The most notable and frequently used side effect is an API call. We will mimic the async nature of an API call using a setTimeout function. We will make a fake API request on the component’s mount that will initialize a random integer 1–10 to our count after waiting 1 second. We will also have an additional useEffect that will update the document title (a side effect) with the current count to show how it responds to a change in state.

The useEffect hook takes a function as an argument. useEffect replaces the componentDidMount, componentDidUpdate, and componentWillUnmount class methods. When the state of the component mounts or updates, React will execute the callback function. If your callback function returns a function itself, React will execute this during componentWillUnmount.

First, let’s create our effect to update the document title. Inside the body of our function, we declare useEffect which sets document.title = ‘Count = ‘ + count in the callback.

When the state count updates, you should see your tab title also updating simultaneously.

For the final step, we will create a mock API call that returns an integer to update the state count. We use a setTimeout and a function that returns a Promise because this simulates the time required to wait for an API request to return and the associated return value of a promise, which allows us to handle the response asynchronously.

To mock an API, we create a mockApi function above our component. It returns a promise with a resolved random integer between 1 and 10.

A common pattern is to make fetch requests in the componentDidMount. To reproduce this is in our functional component, we will add another useState to manage a hasFetched variable: const [hasFetched, setFetch] = useState(false). This is used to prevent the mockApi from being executed on subsequent updates.

Our fetch hook will be an async function, so we will use async/await to handle the result. Inside our useEffect function, we will first check if the hasFetched has been executed. If it has not, we call mockApi and setCount with a result to initialize our value and then flip our hasFetched flag to true.

Visual indicators are essential for UX and provide feedback for your users of the application status. Since we are waiting for an initial count value, we want to hide our buttons and display “Loading…” text on the screen if the hasFetched is false.

This results in the following behavior:

The final code

Wrapping Up

This article introduced hooks and showed how to implement useState and useEffect to simplify your class components into simple functional components. While this is a big win for React developers, the power of hooks is fully realized with the ability to combine them to create custom hooks. This allows you to extract logic and build modular functionality that can seamlessly be shared among React components without the overhead of HOCs or render props. You simply import your custom hook function, and any component can implement it. The only caveat is that all hook functions must follow the rules of hooks.

Author Bio

Trey Huffine

A JavaScript fanatic. He is a software engineer in Silicon Valley building products using React, Node, and Go. Passionate for making the world a better place through code.

Read Next

Reactive programming in Swift with RxSwift and RxCocoa [Tutorial]

React 16.8 releases with the stable implementation of Hooks

PrimeReact 3.0.0 is now out with Babylon create-react-app template