Lesson 8

独自Hooksの作成

Lesson 8 Chapter 1
カスタムフック

これまでにProps、State、レンダリング後の処理の実行方法、レンダリングの最適化方法等を学習してきました。レッスン7ではグローバルなStateについて学びました。本レッスンではカスタムフックについて学習していきます。

カスタムフックとは

フックとは、React16.8から追加されたクラスを使用せずにReactを書くことができる機能のことを指します。英語では"Hooks"と表記します。Reactにおいて、現在主流の関数コンポーネントに従来使用されていたクラスコンポーネントと同じ機能を提供するための関数を集めたライブラリのようなものです。カスタムフックは、自作したロジック(データ取得、サインイン処理等)をまとめて扱いやすいように自作したフックを指します。カスタムフックの使用により、コンポーネントからロジックを分離することや、ロジックを再利用したりすることが可能になります。なおこれまでに紹介してきた以下の機能はReactに標準で搭載されているフックになります(このような理由で使用するファイルの最初でReactからimportしていました。)。

  • useState
  • useEffect
  • useCallback
  • useMemo
  • useContext

以上のフックを見て分かるように、全て"use . . ."と命名されています。カスタムフックも"use"から始まる名前にすることが推奨されています。

カスタムフックが必要な場面

以下のような場面において、カスタムフックの作成を検討すべきです。

  • コンポーネントに複雑なロジックが含まれている:コンポーネント内に複雑なロジックがある場合にカスタムフックに分離することで、コンポーネントがシンプルになり再利用性が向上します。
  • 複数のコンポーネントで共有されるロジックが存在する:同じロジックを複数のコンポーネントで使用する場合にそのロジックをカスタムフックにまとめることで、コードの重複を避けることができるとともに、ロジックの保守性を高めることができます。
  • ロジックにAPIの呼び出しやタイマー処理などを含まれている:API呼び出しやタイマー処理などを含むロジックはカスタムフックに分離することで、コンポーネントがシンプルになり再利用性が向上します。

カスタムフックの実装

では実際にカスタムフックを実装していきたいと思います。準備として、以下のフォルダ構成を参考にカスタムフック保存用のフォルダ"hooks"を作成しておきましょう。

.
└── プロジェクトフォルダ
    ├── public
    │   ├── index.html
    │   └── (その他)
    ├── src
    │   ├── App.jsx
    │   ├── index.jsx
    │   ├── hooks ← 新規作成
    │   └── (その他)
    └── (その他)

レッスン5でJSONPlaceholder からデータを取得するためのApp.jsxを作成しました。このコンポーネントのロジックをカスタムフックとすることで、コンポーネントをシンプルにしたいと思います。当該のApp.jsxを以下に再掲します。

App.jsx
import { useState, useEffect } from "react";
import axios from "axios";
                      
const App = () => {
                      
  const [isSuccess, setIsSuccess] = useState(false);
                      
  useEffect(()=>{axios.get('https://jsonplaceholder.typicode.com/users')
    .then((response)=>{
      console.log(response);
      setIsSuccess(true);
    }).catch((error)=>{
      console.error(error);
    })
    }, []
  );
                          
  return(
    <p>{isSuccess?"データ取得成功":"データ取得失敗"}</p>   
  );
                      
};
                      
export {App};

App.jsxにおいて、データ取得の成否を表す"isSuccess"のStateとデータ取得を実行するuseEffectの第一引数の関数内部の"axios.get( . . . ).then( . . . ).catch( . . . )"の処理は他のコンポーネントにおいても使えそうな汎用性が高い部分であり、カスタムフックとして分離するとよさそうです。カスタムフックを"useGetData"と命名して、以下のuseGetData.jsを作成してみましょう。

useGetData.js
import { useState } from "react";
import axios from "axios";
                      
const useGetData = () => {
                      
  const [isSuccess, setIsSuccess] = useState(false);
                      
  const getData = (url) => { 
    axios.get(url)
      .then((response)=>{
        console.log(response);
        setIsSuccess(true);
      }).catch((error)=>{
        console.error(error);
      });
  };
                          
  return { isSuccess, getData };
                      
};
                      
export { useGetData };

作成が完了したらhooksのフォルダに保存しましょう。useGetData.jsの作成の流れを説明します。最初にuseStateとaxiosを使用するので、それぞれreactとaxiosからimportします。続いて以下の手順で作成していきます。

  1. App.jsxのisSuccessとsetIsSuccessの定義部、useEffectの第一引数の関数をuseGetData.jsに移行する。移行したuseEffectの第一引数の関数はgetData関数として定義する。
  2. 2カスタムフックの作成1.png App.jsxのisSuccess、setIsSuccess、useEffectの第一引数の関数をuseGetData.jsに移行

  3. getData関数の引数"url"を作成し、axios.get()の引数を"url"で置き換える。
  4. 2カスタムフックの作成2.png axios.get()の引数をgetData関数の引数"url"で置き換え

  5. isSuccessとgetDataをカスタムフックから取得できるよう、returnするオブジェクトの内容として関数最後に記述する。
  6. useGetDataを別のファイルで使えるよう、ファイルの最後でexportする。

getData関数の中身は元々AppコンポーネントにあったuseEffectの第一引数の関数の中身とほぼ同じですが、"axios.get( . . . )"の" . . . "を引数urlに置き換えることで、データを取得するURLを使用するコンポーネントごとに任意に設定できるようにしました。これはカスタムフックを汎用的に使用できるようにするための工夫です。以上でカスタムフックの作成は完了です。作成したuseGetDataを使ってApp.jsxファイルを以下のように書き換えましょう。

App.jsx
import { useEffect } from "react";
import { useGetData } from "./hooks/useGetData";
                      
const App = () => {
                      
  const { isSuccess, getData } = useGetData();
                      
  useEffect(()=>{
    getData('https://jsonplaceholder.typicode.com/users');
    }, []
  );
                          
  return(
    <p>{isSuccess?"データ取得成功":"データ取得失敗"}</p>   
  );
                      
};
                      
export {App};

App.jsxにおける変更点を順に確認していきます。まず元々あったuseStateとaxiosをimportする記述を削除しています。代わりに今回作成したカスタムフックuseGetDataをhooksフォルダからimportする記述を追加しています。Appコンポーネント内部では元々、最初にuseStateでisSuccessとその更新関数を定義していましたが、その記述を削除し、代わりにuseGetData()を実行し、isSuccessとgetDataを取得しています。その下のuseEffectについては、第一引数の関数の中身を削除し、https://jsonplaceholder.typicode.com/を引数としてgetData関数を実行する記述に変更しています。

動作確認

プログラムを実行し、カスタムフック使用前後で同じ機能を再現出来ているかを確認します。以下の2枚の画像はカスタムフックを使用する前のブラウザでの表示です。最初の画像はaxios.get関数がデータ取得に成功した場合、後の画像はデータ取得に失敗した場合です。

2023-02-24-13-38-57_.png "データ取得成功"のメッセージとともにコンソールに取得したデータが表示されています。

2023-02-24-16-36-39_.png "データ取得失敗"のメッセージとともにコンソールにエラーメッセージが表示されています。

最初の画像では"データ取得成功"のメッセージとともにコンソールに取得したデータが表示されています。後の画像では"データ取得失敗"のメッセージとともにコンソールにエラーメッセージが表示されています。

以下の2枚の画像はカスタムフックを使用した後のブラウザでの表示です。最初の画像はaxios.get関数がデータ取得に成功した場合、後の画像はデータ取得に失敗した場合です。

2023-03-02-13-59-03_.png "データ取得成功"のメッセージとともにコンソールに取得したデータが表示されています。

2023-02-24-16-36-39_.png "データ取得失敗"のメッセージとともにコンソールにエラーメッセージが表示されています。

データ取得成功失敗いずれの場合においても、同じ結果を得ることができています。このようにカスタムフックを使用することで、コンポーネントからロジックを分離することができ、コンポーネントのシンプルに記述することやロジックを再利用することが可能になります。