Tutorial

How To Apply React Hooks in a React Project

JavaScriptReact

Introduction

In this article, you will explore React Hooks, a new feature in React’s latest version 16.8. React Hooks are functions and serve as a modular replacement for state and lifecycle methods. Instead of class components, React Hooks allow you to build functional-based components.

Prerequisites

To complete this tutorial, an understanding of React is required. To learn more about React, check out the How To Code in React series.

Analyzing the useState() Method

In a class component, you would import React into an index.js file and create an instance of a class object JustAnotherCounter. You would add a state and a function to update the property count:

index.js
import React, { Component } from 'react';

class ScoreCounter extends Component {
  state = {
    count: 0
  };

  setCount = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <div>
        <h1>{this.state.count}</h1>

        <button onClick={this.setCount}>Count Up To The Moon</button>
      </div>
    );
  }
}

In your render lifecycle method, you would return the value held in state and a button that invokes your function through each click.

Let’s compare your ScoreCounter class component as a functional component.

With React Hooks, you can condense your state and lifecycle methods into a functional component in your index.js file with fewer lines of code. Import useState into a destructured object:

index.js
 import React, { useState } from 'react';

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

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => setCount(count + 1)}>Count Up To The Moon</button>
    </div>
  );
}

The useState() method destructures your state object into an array and assigns its elements the value of invoking the hook. The first element is the state, while the second is a function that operates as the .setState() method. The argument you pass into the useState() method acts as the initial state value, here the number 0 as your counter. You can pass in any data type such as arrays, objects, strings, and numbers as the initial state value.

With each click of your button, the setCount() function passes in your state as an argument that increments by 1.

Now, your ScoreCounter functional component supports less syntax in favor of code readability.

You can also use the useState() method multiple times in the same function. In your index.js file, import useState and declare the functional component Login:

index.js
import React, { useState } from 'react';

function Login() {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  return (
    <div className="login">
      <form>
        <label>
            Username: <input value={username} onChange={event => setUsername(event.target.value)}/>
            <br />
            Password: <input value={password} onChange={event => setPassword(event.target.value)}/>
          </label>
        <button>Submit</button>
      </form>
    </div>
  );
}

The Login functional component employs the useState() method multiple times to define and set a username and password in a form, and outlines logic for each input field.

With an understanding of useState(), let’s consider other React Hook methods.

Using the useEffect() Method

React Hooks introduces the useEffect() method to replace a class component’s lifecycle methods componentDidMount, componentDidUpdate, and componentWillUnmount. The method also allows side effects in your functional component, such as changing content in the document object model and fetching data. useEffect() will run after every component render.

Let’s compare a class’s lifecycle method call with a functional component.

In your index.js file, import useEffect into your ScoreCounter component:

index.js
 import React, { useState, useEffect } from 'react';

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

  useEffect(() => {
    console.log(count); 
    document.title = `Your new score is ${count}`;
  });

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => setCount(count + 1)}>Count Up To The Moon</button>
    </div>
  );
}

Here, useEffect() produces the side effects of logging into the console to check for the initial state value in count, and updating the document object model.

Since useEffect() runs each time a component renders, you can set a second argument, an array, to store each instance and call on the method if a value has changed.

In your index.js file, apply an empty array as the second argument to your useEffect() method:

index.js
 import React, { useState, useEffect } from 'react';

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

  useEffect(() => {
    console.log(count); 
    document.title = `Your new score is ${count}`;
  }, []); 
}

With each render to your ScoreCounter component, the useEffect() method will store each instance of count in the array and update the component if the value differs from a previous call. In one call, useEffect() combines the functionality in the componentDidMount and componentDidUpdate lifecycle methods.

To simulate the componentWillUnmount lifecycle method, return a function in the useEffect() method in your index.js file:

index.js
 import React, { useState, useEffect } from 'react';

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

  useEffect(() => {
    console.log(count); 
    document.title = `Your new score is ${count}`;

    return () => console.log(`Component is unmounted`);
  }, []); 
}

By declaring and returning an anonymous function, the useEffect() method unmounts a component in one call, here with a console.log to check for its status.

To fetch data, pass fetch within useEffect() to get information from an external API.

In your index.js file, import useState and useEffect. Declare a GitHubUsers functional component, and pass in fetch within the body of useEffect:

index.js
import React, { useState, useEffect } from 'react';

function GitHubUsers() {
  const [user, setUsers] = useState([]);

  useEffect(() => {
    fetch('https://api.github.com/users')
      .then(response => response.json())
      .then(data => {
        setUsers(data); // set users in state
      });
  }, []); 

Notice the second argument set as an empty array. This is to inform useEffect() to run once. The useEffect() method will invoke fetch to call the GitHub API and set the response into a JSON object.

Once the call is successful, include a return statement in your GitHubUsers component to iterate through the data and output the GitHub user information:

index.js
  return (
    <div className="section">
      {users.map(user => (
        <div key={user.id} className="card">
          <h5>{user.login}</h5>
        </div>
      ))}
    </div>
  );
}

The useEffect() method takes the functionality within all three of a class component’s lifecycle methods in one call to log into the console, update the document object model, and fetch data from an external API.

Conclusion

The useState() and useEffect() methods are powerful additions to the React library. Now with React Hooks, you can maximize your functional components with state and lifecycle methods in shorter lines of code.

For more information on React Hooks, check out the How To Manage State with Hooks on React Components article.

Creative Commons License