15個の役立つカスタムReactフック

カスタムReactフックを使えば、関数コンポーネントで再利用可能なロジックを実装し、コンポーネントを分離し、その目的に集中させる小さな部分を維持できます。カスタムフックは、コンポーネント自体とは別に隔離されてテストすることで、コンポーネントのロジックを理解したりテストしたりしやすくなります。また、カスタムフックを使うと、異なるコンポーネント間でロジックを共有することが容易になり、コードの重複を減らして、コードベースの保守とアップデートがしやすくなります。

Reactフックには無限の可能性がありますが、作られたものがすべて同じとは限りません。この記事では、どんなプロジェクトでも使えて、導入が超簡単な強力なカスタムフックを15個紹介します。

注意: これらのフックは、YoutuberのWeb Dev Simplifiedが作成したものです。詳しいビデオ解説が欲しい場合は、彼のビデオをチェックしてください。ただし、読むだけでいい場合は、このまま続けてください。

目次


  1. useToggle

import { useState } from "react";

export default function useToggle(defaultValue) {
  const [value, setValue] = useState(defaultValue);

  function toggleValue(value) {
    setValue(currentValue =>
      typeof value === "boolean" ? value : !currentValue
    );
  }

  return [value, toggleValue];
}

useToggleは、値をtrueとfalseの間で切り替えることを可能にするカスタムReactフックです。useStateフックを使用して状態を管理します。まず、フックはdefaultValue引数を受け取り、値の状態を初期化します。次に、現在の値と、値をtrueとfalseの間で切り替える関数であるtoggleValueを含む配列を返します。この関数は1つのパラメータを受け取ります。パラメータがブール型の場合はその値を設定し、そうでない場合は現在値をトグルします。

このフックの使い方の例です:

import useToggle from "./useToggle";

export default function ToggleComponent() {
  const [value, toggleValue] = useToggle(false);

  return (
    <div>
      <div>{value.toString()}</div>
      <button onClick={toggleValue}>Toggle</button>
      <button onClick={() => toggleValue(true)}>Make True</button>
      <button onClick={() => toggleValue(false)}>Make False</button>
    </div>
  );
}
  1. useTimeout

import { useCallback, useEffect, useRef } from "react";

export default function useTimeout(callback, delay) {
  const callbackRef = useRef(callback);
  const timeoutRef = useRef();

  useEffect(() => {
    callbackRef.current = callback;
  }, [callback]);

  const set = useCallback(() => {
    timeoutRef.current = setTimeout(() => callbackRef.current(), delay);
  }, [delay]);

  const clear = useCallback(() => {
    timeoutRef.current && clearTimeout(timeoutRef.current);
  }, []);

  useEffect(() => {
    set();
    return clear;
  }, [delay, set, clear]);

  const reset = useCallback(() => {
    clear();
    set();
  }, [clear, set]);

  return { reset, clear };
}

useTimeoutは、コンポーネントがタイムアウトをset(設定)し、clear(クリア)することを可能にするカスタムReactフックです。useCallbackuseEffect、およびuseRefフックをReactライブラリから使用します。フックは2つの引数を取ります:特定の遅延後に呼び出されるcallbackと、callbackが呼び出されるまでに経過すべきミリ秒単位の時間を示すdelayです。

フックは2つのプロパティを含むオブジェクトを返します:resetclearは、タイムアウトをリセットまたはクリアするために使用できる関数です。

フックはuseRefフックを使って2つのリファレンスを作成します:callbackReftimeoutRefです。callbackRefcallback関数を保持するミュータブルな値として、timeoutRefにはsetTimeout()関数によって返されたタイムアウトIDを格納します。

useEffectフックはcallbackRef.currentが常に最新のcallbackを持っているようにするために使用されます。

set関数はsetTimeoutを使って新しいタイムアウトを作成し、指定された遅延後にcallback関数を呼び出します。clear関数はclearTimeoutを使ってタイムアウトをクリアします。そして、もう1つのuseEffectフックがマウント時にタイムアウトを設定し、アンマウント時にそれを削除するために使用されます。reset関数はclearset関数の組み合わせです。最後に、useCallbackフックがその依存関係が変わったときにのみ関数が再作成されることを保証します。

このフックの使い方の例です:

import { useState } from "react";
import useTimeout from "./useTimeout";

export default function TimeoutComponent() {
  const [count, setCount] = useState(10);
  const { clear, reset } = useTimeout(() => setCount(0), 1000);

  return (
    <div>
      <div>{count}</div>
      <button onClick={() => setCount(c => c + 1)}>Increment</button>
      <button onClick={clear}>Clear Timeout</button>
      <button onClick={reset}>Reset Timeout</button>
    </div>
  );
}

このカスタムuseTimeoutフックは、あるコンポーネントが避けられない遅延を必要とするさまざまな状況で役立ちます。例えば:

  • 一定時間後に消える通知メッセージ
  • 一定時間ローディングスピナーが表示され、その後リダイレクトするフォームの送信
  • 一定時間後に自動的に次のスライドに進むスライドショー
  • カウントダウンタイマーが残り時間を表示し、ゼロに達するとアクションをトリガーする
  • 一定時間後にフォームデータを自動保存するオートセーブ機能
  • 一定時間の非活動後にユーザーをログアウトするセッションタイムアウト
  • 一定時間後にコールバック実行を遅らせるデバウンス機能

特定の時間を待ってから行動を起こす必要がある場合や、遅延を挟んで同じアクションを複数回繰り返したい場合に使用することができます。

  1. useDebounce

import { useEffect } from "react";
import useTimeout from "../2-useTimeout/useTimeout";

export default function useDebounce(callback, delay, dependencies) {
  const { reset, clear } = useTimeout(callback, delay);
  useEffect(reset, [...dependencies, reset]);
  useEffect(clear, []);
}

useDebounceは、特定の時間が経過するまでコールバック関数の実行を遅らせることを可能にするカスタムReactフックです。Reactライブラリから組み込みのuseEffectフックを使用し、useTimeout(2番目のカスタムフック)カスタムフックを使用します。

このフックは3つの引数を取ります:

  • "callback"はデバウンスされるべき関数です。
  • "delay"はコールバックが呼び出されるまでに経過すべきミリ秒単位の時間です。
  • "dependencies"は、変更があった場合に再度コールバックを実行するためにフックが監視する値の配列です。

フックはuseTimeoutフックを使ってタイムアウトを作成し、特定の遅延が経過した後にcallback関数を呼び出します。useEffectフックを使って、マウント時にタイムアウトを設定し、アンマウント時にクリアします。最初のuseEffectは依存関係が変わるたびにreset関数を呼び出し、2番目のuseEffectはコンポーネントのアンマウント時にclear関数を呼び出します。

このフックの使い方の例です:

import { useState } from "react";
import useDebounce from "./useDebounce";

export default function DebounceComponent() {
  const [count, setCount] = useState(10);
  useDebounce(() => alert(count), 1000, [count]);

  return (
    <div>
      <div>{count}</div>
      <button onClick={() => setCount(c => c + 1)}>Increment</button>
    </div>
  );
}

短時間にコールバック関数が何度も呼び出されるのを制限したい場合に役立ちます。たとえば、すべてのキーストロークでサーバーに検索リクエストを送信する入力フィールドがある場合、必要のないネットワークトラフィックを避け、ユーザーの操作体験を向上させるために、リクエストを送信する前にユーザーがタイピングを止めるのを待つべきです。

  1. useUpdateEffect

import { useEffect, useRef } from "react";

export default function useUpdateEffect(callback, dependencies) {
  const firstRenderRef = useRef(true);

  useEffect(() => {
    if (firstRenderRef.current) {
      firstRenderRef.current = false;
      return;
    }
    return callback();
  }, dependencies);
}

useUpdateEffectは、特定の依存関係が変更されたときにのみコールバック関数を実行するようにするカスタムReactフックです。Reactライブラリに組み込まれているuseEffectuseRefフックを使用します。

フックは2つの引数を取ります:

  • callbackは依存関係が変わったときに呼ばれるべき関数です。
  • dependenciesはフックが変更を検知して反応し、コールバックを実行すべき値の配列です。

フックはuseRefフックを使用してfirstRenderRefという名前のリファレンスを作成します。これは初回レンダリングの時にtrueとして初期化されます。このリファレンスを使用してコンポーネントの最初のレンダリングを追跡します。

useEffectフックは依存関係の配列の変更を監視し、callback関数を呼び出します。useEffect関数内部では、firstRenderRefの値をチェックして、最初のレンダリングかどうかを確認します。もしそうなら、falseに設定し、それ以外の場合は変更があったのでコールバック関数を呼び出します。

このフックの使い方の例です:

import { useState } from "react";
import useUpdateEffect from "./useUpdateEffect";

export default function UpdateEffectComponent() {
  const [count, setCount] = useState(10);
  useUpdateEffect(() => alert(count), [count]);

  return (
    <div>
      <div>{count}</div>
      <button onClick={() => setCount(c => c + 1)}>Increment</button>
    </div>
  );
}

依存関係のある特定の値が変更されたときだけロジックを実行したい場合、例えばユーザーがドロップダウンメニューから特定のオプションを選択した後にAPIからデータを取得する場合や、ウィンドウのサイズが変わった後に画面上の要素の位置を更新する場合などに役立ちます。

  1. useArray

import { useState } from "react";

export default function useArray(defaultValue) {
  const [array, setArray] = useState(defaultValue);

  function push(element) {
    setArray(a => [...a, element]);
  }

  function filter(callback) {
    setArray(a => a.filter(callback));
  }

  function update(index, newElement) {
    setArray(a => [
      ...a.slice(0, index),
      newElement,
      ...a.slice(index + 1, a.length),
    ]);
  }

  function remove(index) {
    setArray(a => [...a.slice(0, index), ...a.slice(index + 1, a.length)]);
  }

  function clear() {
    setArray([]);
  }

  return { array, set: setArray, push, filter, update, remove, clear };
}

useArrayは、コンポーネントが配列状態を管理することを可能にするカスタムReactフックです。Reactライブラリから組み込みのuseStateフックを使います。フックは引数defaultValueを取り、配列状態の初期化に使用されます。フックはいくつかのプロパティを含むオブジェクトを返します:

  • arrayは現在の配列状態です
  • setは配列状態を新しい値に設定するための関数です
  • pushは配列の最後に要素を追加するための関数です
  • filterはコールバック関数を渡すことで配列をフィルタリングするための関数です
  • updateは配列の特定のインデックスの要素を更新するための関数です
  • removeは配列の特定のインデックスの要素を削除するための関数です
  • clearは配列をクリアするための関数です

    こちらの記事はdev.toの良い記事を日本人向けに翻訳しています。
    https://dev.to/arafat4693/15-useful-react-custom-hooks-that-you-can-use-in-any-project-2ll8