React は ユーザインターフェース構築のための JavaScript ライブラリ を名乗っています。React 上ではしばしばサーバからマスタデータ全体を取得する必要があります。よくこれをする必要があるのが次のER図の様なデータ構造において、子のデータに紐づく親を選択したい時です。このデータ構造は会員種別が不定のため、会員に紐づく会員種別をブラウザやアプリの上で定めるには都度サーバから取得する必要があります。
これは次の様に axios とローディングをカスタムフックの中にまとめると扱いやすいです。
import React, { useState } from 'react'; import { useAdminAxios } from '@/admin/hook/useAdminAxios'; // select 要素内部の option 要素でよく使う形を定義した型 type SelectOption<V = number | string> = { label: string; value: V; selected?: boolean; }; /** * 使用してマウントされた時にだけ axios でサーバ上のマスターを取りに行くフック */ export const useMemberTypeOptions = () => { // プログラムの外から取得して、フックの外に渡す値の state const [typeOptions, setTypeOptions] = useState<SelectOption[]>([]); // 非同期通信中か否かを示すためのstate。最初に一度だけロードするフックなので最初からローディング状態 const [isLoading, setLoading] = useState(true); React.useEffect(() => { // 一度だけ実行して二度と実行しないuseEffect axios.get('/member_type_options') .then((response) => setTypeOptions(response.data)) .catch((error) => /** axios共通のエラーハンドリング */) .finally(() => setLoading(false));// 成功しても失敗しても必ずローディングをオフ }, []); return {// 外には必要な値とローディング中か否かのみの最小限のみを露出(setterが必要な部分はフック内で完結) typeOptions, typeOptionsLoading: isLoading, }; };
こうすると次の様に会員種別の取得コードを短く、分かりやすく書けます。
const MemberSearchPage: React.FC = () => { const { typeOptionsLoading, typeOptions } = useMemberTypeOptions(); if (typeOptionsLoading) { return <AppLoading message={'ロード中です'} />;// ローディング表示用コンポーネント } return ( 会員種別を使うコンポーネント本体 ) }
これの良い点は主に2点あり、1つは axios の非同期通信操作部のロジックと画面を描画するコンポーネント部を完全に分離できる部分です。通信のリクエスト、レスポンスの加工、ハンドリングはフック内に任せて、コンポーネントは必要な情報と必要な情報がロード済みか否かのみを気にすることができます。
もう1つは呼び出し側のコードが簡素な点です。引数もないuseHoge()のフック呼び出し用一行コードだけで済みます。ロード画面を作らないなら例よりも更に少なくなります。コンポーネントのレンダリング前のロジック部の複雑化を防げます。
axios の呼び出しに限らず何かしらのロジックの一塊をコピペしだしたら上述の例の様にフックに切り出せないか考えてみるとコードを整理しやすいです。
フックの導入 – React
独自フックの作成 – React#カスタムフックの抽出