浜松のWEBシステム開発・スマートフォンアプリ開発・RTK-GNSS関連の開発はお任せください
株式会社シーポイントラボ
TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:静岡県浜松市中区富塚町1933-1 佐鳴湖パークタウンサウス2F

【React】axios をカスタムフック化

 axios は HTTP client ライブラリで外部との通信を非同期で行います。
axios/axios: Promise based HTTP client for the browser and node.js
 axios を使った時、通信の開始から終了までローディングを示す何かしらを表示することが多いです。これをメソッドチェーンで素朴に記述すると次の様になります。

// チェーンで記述すると
setIsLoading(true);
axios.get('hoge').then((response) => {
  // 成功時処理
}).catch(error) => {
  // 失敗時処理
}).finally(()=>setIsLoading(false));
// もし isLoading をコンポーネントの state として扱う && 成功時か失敗時のどちらか片方の場合のみ画面遷移する ならば
// finally に setIsLoading(false) は書くべきでないです。React にメモリリークの危険があると警告されます。

 ローディング終了処理の記述忘れが起きそうです(何度かやらかしました)。setIsLoading()を無用にすることでこの問題を解決します(他の解としては async/await で setIsLoading(true); await 通信処理(); setIsLoading(false); の定型を記述する方法も有力です)。
 React のカスタムフックはコンポーネントに関わらないロジックのみを抽出しつつ React の他のフックを使う関数です。
独自フックの作成 – React
 ある関数がフックであるか否かは関数名が use で始まるか否かで定まります。
 axios の通信状態に関わる処理をフック内でまとめることで setIsLoading をコンポーネントから消します。具体的には次です。

import { useState } from 'react';
// あらかじめ他で axios.create(独自設定) して作られた axiosInstance
import { BaseRepository } from '@/repository/BaseRepository';
import { AxiosError, AxiosInstance, AxiosResponse } from 'axios';

/**
 * axios を使うカスタムフック. 引数で成功時処理、失敗時処理を記述することも可能
 */
export function useAxios(
  successHookCallback?: (response: AxiosResponse) => void,
  failedHookCallback?: (error: AxiosError) => void
): { isLoading: boolean; axiosInstance: AxiosInstance } {
  const axiosInstance = BaseRepository;
  // ローディングを state 化
  const [isLoading, setIsLoading] = useState(false);
  // リクエストが投げられた時にローディングを true に変更
  axiosInstance.interceptors.request.use((request) => {
    setIsLoading(true);
    return request;
  });
  // レスポンスを受け取った時にローディングを false に変更
  axiosInstance.interceptors.response.use(
    (response) => {
      setIsLoading(false);
      typeof successHookCallback === 'function' && successHookCallback(response);
      return response;
    },
    (error) => {
      setIsLoading(false);
      typeof failedHookCallback === 'function' && failedHookCallback(error);
      throw error;
    }
  );

  // 外部にはローディング状態とaxiosInstanceだけ見せる
  return {
    isLoading,
    axiosInstance,
  };
}

 BaseRepository は以前書いた記事にある様に生成された様々な設定を付与した axios のインスタンスです。
【JavaScript】axiosでAPIに関する常の認証や例外を一か所で定義 – 株式会社シーポイントラボ | 浜松のシステム・RTK-GNSS開発
 こうすると次の様に useAxios するだけで必要な情報のみをコンポーネントで扱えます。

  // pushMessage は画面共通通知用関数です
  // @see https://cpoint-lab.co.jp/article/202008/16339/【React】コンテクストを使った props を用いない関数一つで呼び出せる通知用コンポーネント例 – 株式会社シーポイントラボ | 浜松のシステム・RTK-GNSS開発
  const { isLoading, axiosInstance } = useAxios(
    () => pushMessage('保存しました。'),
    () => pushMessage('保存に失敗しました。')
  );

  const store = () => {
    axiosInstance
      .post('user/post', {/** 必要な情報 */})
      .then((response) => {
        // 通信成功固有処理
      });
  };
  
  
  return (
    <Button style={{ height: 'auto' }} onClick={store}>
      {isLoading ? <LoadingSpinner message={'保存中'} /> : '保存'}
    </Button>
  )

 こんな感じでロジックをフックに抜き出していくとコンポーネントには画面表示に必要な情報のみが残り、いい感じにコードが少なく済み、整理もされます。

  • この記事いいね! (0)