Introduction to React custom hooks

A brief guide

Posted by Jiali on March 28, 2022

Introduction to React custom hooks

In this article, we will introduce what are React custom hooks, how to build and use custom hooks, and when to use them.

What is a custom hook?

Since React 16.8, Hooks are introduced. Custom hooks, just as React built-in hooks, are JavaScript functions. Custom hooks are built by ourselves, with our defined logic and functionalities. Inside custom hooks, we can use other React hooks. In this way, we can extract component logic into reusable functions.

How to build a custom hook

As React defined, A custom Hook is a JavaScript function whose name starts with use and it calls other Hooks.

Note: It’s very important to follow the naming convention. It tells React that the rules of Hooks apply to it. Otherwise, it is only a normal function. React can’t tell if the custom hook contains calls to Hooks inside of it and may cause errors.

Consider if we have a form component that allows users to input first and last names, we will use multiple states to handle the change.

function NameForm() {
  const [enteredFirstName, setEnteredFirstName] = useState('');
  const [enteredLastName, setEnteredLastName] = useState('');

  const firstNameChangeHanlder = (event) => {
    setEnteredFirstName(event.target.value);
  };
  const lastNameChangeHanlder = (event) => {
    setEnteredLastName(event.target.value);
  };

  return (
    <form>
      <label htmlFor="name">First Name</label>
      <input
        type="text"
        value={enteredFirstName}
        onChange={firstNameChangeHanlder}
      />

      <label htmlFor="name">Last Name</label>
      <input
        type="text"
        value={enteredLastName}
        onChange={lastNameChangeHanlder}
      />
    </form>
  );
}

As you can see, it uses two useState hooks to update input changes for each input. Also, those codes repeat the same logic. So, we would like to extract it into a custom hook. Here’s how we transform it into a custom hook.

import React, { useState } from 'react';

function useTextInput() {
  const [enteredValue, setEnteredValue] = useState('');

  const valueChangeHandler = (event) => {
    setEnteredValue(event.target.value);
  };

  return {
    value: enteredValue,
    valueChangeHandler,
  };
}

export default useTextInput;

Use the custom hook

In the NameForm component, we can now use our custom hook. We import useTextInput and deconstruct return values of useTextInput. Then, in the JSX codes, we use these variables.

import useTextInput from '../hooks/use-input';

function NameForm() {
  const { value: firstNameValue, valueChangeHandler: firstNameChangeHanlder } =
    useTextInput();

  const { value: lastNameValue, valueChangeHandler: lastNameChangeHanlder } =
    useTextInput();

  return (
    <form>
      <label htmlFor="name">First Name</label>
      <input
        type="text"
        value={firstNameValue}
        onChange={firstNameChangeHanlder}
      />

      <label htmlFor="name">Last Name</label>
      <input
        type="text"
        value={lastNameValue}
        onChange={lastNameChangeHanlder}
      />
    </form>
  );
}

export default NameForm;

Moreover, you can also pass parameters and functions in custom hooks. Here we allow the useTextInput to accept a value variable and a validate function.

function useTextInput(value, validate) {
  const [enteredValue, setEnteredValue] = useState(value);

  const isValid = validate(enteredValue);

  const valueChangeHandler = (event) => {
    setEnteredValue(event.target.value);
  };

  return {
    value: enteredValue,
    isValid: isValid,
    valueChangeHandler,
  };
}

The passing value will be the initial value of the enteredValue. It uses the validate function to determine if the value is empty.

const {
  value: firstNameValue,
  valueChangeHandler: firstNameChangeHanlder,
  isValid: firstNameValid,
} = useTextInput('Jane', (value) => value.trim() !== '');

To use the custom hook in the NameForm component, you only need to pass ‘Jane’ and a function to useTextInput. That’s how custom hooks accept parameters.

Why and when using custom hooks

The main reason to write a custom hook is for code reusability. From the above example, we can see when components share the same logic, and they all use React hooks inside themselves, custom hooks are a handy way to reduce duplicate codes. We take out the shared codes and refactor them by creating a separate function to hold them. As hooks have two rules:

  • Only Call Hooks at the Top Level
  • Only Call Hooks from React Functions

If we want to extract a component that uses hooks, we will use custom hooks. It keeps components simple and enables the shared logic to be used among components. As a result, it improves code reusability. Besides, it also isolates the testable logic and ensures testing.

For offical reference: React documentation