Contents
React 18対応のカスタムフックとは?
React 18においても、カスタムフックは依然として重要な役割を果たしています。カスタムフックは、useStateやuseEffectといったReactの基本的なHooksを組み合わせて、コンポーネント間で再利用可能なロジックを構築する仕組みです。特に**useから始まる命名規則に従った関数を作成することで、他の開発者にとっても読みやすく、保守性が向上します。
useHook命名規則の重要性
カスタムフックの名前は必ずuseから始める必要があります。これにより、Reactの内部的なHooksと区別しやすくなり、コードリーダーにとって「この関数は副作用を持つロジックである」と一目で理解できます。
- 例:
useModal,useFetch,useLocalStorage - NG例:
getModalData,handleToggle(Hookとしての目的が不明確)
基本原則と設計思想
カスタムフックは「ロジックの単位」を表現するための機能です。UIに依存しない汎用的な処理(状態管理、API通信、副作用の制御など)を集約することで、コードの重複を防ぎます。以下が設計時のポイントです。
-
コンポーネントとは独立させる
UIとロジックを分離し、同じロジックを複数のコンポーネントで再利用可能にします。 -
副作用はuseEffectで管理する
API通信や状態変更など、副作用のある処理はuseEffect内で統括します。 -
外部から制御可能なインターフェースを提供する
例:useModalではisOpenというプロパティを返し、外部で状態を操作できるように設計します。
モーダル管理用カスタムフックの実装手順
モーダル(ダイアログ)の表示・非表示を管理するuseModalというカスタムフックを作成してみましょう。このフックは、状態管理と効果フックの連携によって、複数のコンポーネント間で一貫した挙動を実現します。
状態管理と効果フックの連携
useStateでモーダルの表示状態を管理し、useEffectで副作用(例:Escキーでの閉じ操作)を処理します。以下が基本的な実装例です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
function useModal() { const [isOpen, setIsOpen] = useState(false); useEffect(() => { const handleEscapeKey = (e) => { if (e.key === 'Escape') { setIsOpen(false); } }; if (isOpen) { window.addEventListener('keydown', handleEscapeKey); } return () => { window.removeEventListener('keydown', handleEscapeKey); }; }, [isOpen]); const toggle = () => setIsOpen(!isOpen); const close = () => setIsOpen(false); return { isOpen, toggle, close }; } |
useState:モーダルの表示状態を保持します。useEffect:モーダルが開いている時にだけ、Escキーでの閉じ処理を追加します。- 返却値:
isOpen,toggle,closeというインターフェースを外部に公開します。
外部から制御可能なインターフェース設計
他のコンポーネントでこのフックを使う際は、以下のように呼び出せます。
|
1 2 |
const { isOpen, toggle, close } = useModal(); |
toggle:モーダルの表示・非表示を切り替えます。close:強制的に閉じる処理です。
このように、カスタムフックは「ロジック」と「UI」の分離を図りつつ、外部とのインタラクションを明確に定義することで再利用性を高めます。
API取得処理をカプセル化したuseFetchの実装
REST APIなどの非同期通信は、fetchやaxiosを使って行いますが、これをカスタムフックで統一すると、コードの見通し良さが向上します。以下はReact 18対応のuseFetchの実装例です。
非同期処理の標準パターン
非同期通信ではPromiseベースの処理を行い、APIのステータス(成功・エラー)やロード状態を管理します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
function useFetch<T>(url: string, options?: RequestInit): { data: T | null; loading: boolean; error: Error | null; } { const [data, setData] = useState<T | null>(null); const [loading, setLoading] = useState<boolean>(true); const [error, setError] = useState<Error | null>(null); useEffect(() => { const fetchData = async () => { try { const response = await fetch(url, options); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); const result = await response.json(); setData(result as T); } catch (err) { setError(err instanceof Error ? err : new Error('Unknown error')); } finally { setLoading(false); } }; fetchData(); }, [url, options]); return { data, loading, error }; } |
useEffect:APIの呼び出しを副作用として管理します。- キャッシュ戦略:必要に応じてローカルストレージなどでのキャッシュ処理を追加可能です。
エラーハンドリングとロード状態管理
エラー発生時の挙動を明確に定義し、ロード中かどうかをloadingという状態で提供します。これにより、UI側はデータの取得状況に応じた表示(スピンナー表示など)を行えます。
| 状態 | 説明 |
|---|---|
| data | APIから取得したデータ(存在しない場合はnull) |
| loading | ロード中のフラグ(true/false) |
| error | エラー情報(存在しない場合はnull) |
このフックを使うことで、複数のコンポーネントで同じAPI呼び出し処理を一元管理でき、保守性が向上します。
再利用性向上のための設計ベストプラクティス
カスタムフックは「再利用可能」なロジックを構築するために設計されるべきです。特にTypeScriptとの併用で型定義を行うことで、エラー発生時の補完や検証が容易になります。
型定義による安全性の確保
ReactプロジェクトではTypeScriptを使用することが一般的であり、カスタムフックにも型情報を明示することで、開発中のヒントと保守性が向上します。
|
1 2 3 4 5 6 7 8 9 10 11 |
// useFetch.ts interface UseFetchResult<T> { data: T | null; loading: boolean; error: Error | null; } function useFetch<T>(url: string, options?: RequestInit): UseFetchResult<T> { // ... } |
T:返却されるデータの型を指定します。- 戻り値:
data,loading,errorそれぞれに明確な型を定義します。
これにより、呼び出し側で適切な型が補完され、エラーの早期検出が可能になります。
コンポーネント依存の最小化
カスタムフックは「ロジックのみ」に特化し、UIとの関係性を避けましょう。例えばuseFetchはデータ取得ロジックを提供するだけで、表示方法やアニメーションなどの処理はコンポーネント内で行います。
- 良い例:
useFetchでデータを取得 → コンポーネントでUIに反映 - 悪い例:
useFetchでAPI呼び出しができ、ロード中にはスピンナーを表示する処理も含む(UIとロジックが混在)
このようにして、カスタムフックは汎用的なロジックの共通部として活用し、プロジェクト全体の品質向上に貢献します。
プロジェクトへの適用ガイド
作成したuseModalやuseFetchを実際のプロジェクトに導入する際には、以下のような手順で進めるとスムーズです。
既存プロジェクトの統合手順
- カスタムフックファイルを作成(例:
src/hooks/useModal.ts,src/hooks/useFetch.ts) - コンポーネント内でimportして使用
- テストコードを追加(例:JestやTesting Libraryでカバレッジ率を確保)
注意点:プロジェクトに既存のフックライブラリ(react-queryなど)が導入されている場合は、重複するロジックを避ける工夫が必要です。
テストとデバッグのポイント
- テストコードでのモック対応:
useFetchの場合、fetchの呼び出しをモックしてエラーハンドリングを検証します。 - コンソールログ確認:
errorが正しくキャッチされるか、ロード中の状態遷移が正常かをブラウザの開発者ツールで確認します。
| デバッグ時のヒント | 説明 |
|---|---|
| console.log() | 実行時にデータやエラー情報を出力して挙動を確認する |
| React Developer Tools | コンポーネントの状態変化やフックの動作を可視化できるツール |
このようにして、カスタムフックはプロジェクト全体の開発効率と品質向上に寄与します。