日本語

カスタムフックでReactアプリケーションの再利用可能なロジックの力を解き放ちましょう。よりクリーンで保守性の高いコードを実現するためのカスタムフックの作成と活用方法を学びます。

カスタムフック:Reactにおける再利用可能なロジックパターン

Reactフックは、関数コンポーネントに状態管理とライフサイクル機能をもたらし、Reactコンポーネントの記述方法に革命を起こしました。フックが提供する多くの利点の中でも、カスタムフックは複数のコンポーネント間でロジックを抽出し再利用するための強力なメカニズムとして際立っています。このブログ記事では、カスタムフックの世界を深く掘り下げ、その利点、作成方法、そして実践的な例を用いた使用方法を探ります。

カスタムフックとは?

本質的に、カスタムフックは「use」という単語で始まり、他のフックを呼び出すことができるJavaScript関数です。これにより、コンポーネントのロジックを再利用可能な関数に抽出できます。これは、レンダープロップや高階コンポーネント、その他の複雑なパターンに頼ることなく、ステートフルなロジックや副作用、その他の複雑な振る舞いをコンポーネント間で共有する強力な方法です。

カスタムフックの主な特徴:

カスタムフックを使用する利点

カスタムフックは、React開発においていくつかの大きな利点を提供します。

初めてのカスタムフックを作成する

ウィンドウサイズを追跡するフックという実践的な例で、カスタムフックの作成を説明しましょう。

例:useWindowSize

このフックは、ブラウザウィンドウの現在の幅と高さを返します。また、ウィンドウがリサイズされたときにこれらの値を更新します。

import { useState, useEffect } from 'react';

function useWindowSize() {
  const [windowSize, setWindowSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight,
  });

  useEffect(() => {
    function handleResize() {
      setWindowSize({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    }

    window.addEventListener('resize', handleResize);

    // クリーンアップ時にイベントリスナーを削除
    return () => window.removeEventListener('resize', handleResize);
  }, []); // 空の配列は、エフェクトがマウント時にのみ実行されることを保証する

  return windowSize;
}

export default useWindowSize;

解説:

  1. 必要なフックのインポート: ReactからuseStateuseEffectをインポートします。
  2. フックの定義: 命名規則に従い、useWindowSizeという名前の関数を作成します。
  3. 状態の初期化: useStateを使用して、ウィンドウの初期の幅と高さでwindowSize状態を初期化します。
  4. イベントリスナーの設定: useEffectを使用して、ウィンドウにリサイズイベントリスナーを追加します。ウィンドウがリサイズされると、handleResize関数がwindowSize状態を更新します。
  5. クリーンアップ: useEffectからクリーンアップ関数を返し、コンポーネントがアンマウントされるときにイベントリスナーを削除します。これによりメモリリークを防ぎます。
  6. 戻り値: このフックは、ウィンドウの現在の幅と高さを含むwindowSizeオブジェクトを返します。

コンポーネントでカスタムフックを使用する

カスタムフックを作成したので、それをReactコンポーネントでどのように使用するかを見てみましょう。

import React from 'react';
import useWindowSize from './useWindowSize';

function MyComponent() {
  const { width, height } = useWindowSize();

  return (
    

ウィンドウ幅: {width}px

ウィンドウ高: {height}px

); } export default MyComponent;

解説:

  1. フックのインポート: useWindowSizeカスタムフックをインポートします。
  2. フックの呼び出し: コンポーネント内でuseWindowSizeフックを呼び出します。
  3. 値へのアクセス: 返されたオブジェクトを分割代入して、widthheightの値を取得します。
  4. 値のレンダリング: コンポーネントのUIで幅と高さをレンダリングします。

useWindowSizeを使用するどのコンポーネントも、ウィンドウサイズが変更されると自動的に更新されます。

より複雑な例

カスタムフックのより高度な使用例をいくつか探ってみましょう。

例:useLocalStorage

このフックを使用すると、ローカルストレージからデータを簡単に保存および取得できます。

import { useState, useEffect } from 'react';

function useLocalStorage(key, initialValue) {
  // 値を保存するための状態
  // ロジックが一度だけ実行されるように、初期値をuseStateに渡す
  const [storedValue, setStoredValue] = useState(() => {
    try {
      // キーでローカルストレージから取得
      const item = window.localStorage.getItem(key);
      // 保存されたJSONをパース、なければinitialValueを返す
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      // エラーの場合もinitialValueを返す
      console.log(error);
      return initialValue;
    }
  });

  // useStateのセッター関数をラップしたバージョンを返す...
  // ...新しい値をlocalStorageに永続化する。
  const setValue = (value) => {
    try {
      // useStateと同じAPIを持つように、値を関数にできるようにする
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      // ローカルストレージに保存
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
      // 状態を保存
      setStoredValue(valueToStore);
    } catch (error) {
      // より高度な実装ではエラーケースを処理する
      console.log(error);
    }
  };

  useEffect(() => {
    try {
      const item = window.localStorage.getItem(key);
      setStoredValue(item ? JSON.parse(item) : initialValue);
    } catch (error) {
      console.log(error);
    }
  }, [key, initialValue]);

  return [storedValue, setValue];
}

export default useLocalStorage;

使用法:

import React from 'react';
import useLocalStorage from './useLocalStorage';

function MyComponent() {
  const [name, setName] = useLocalStorage('name', 'Guest');

  return (
    

こんにちは、{name}さん!

setName(e.target.value)} />
); } export default MyComponent;

例:useFetch

このフックは、APIからデータをフェッチするためのロジックをカプセル化します。

import { useState, useEffect } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function fetchData() {
      try {
        const response = await fetch(url);
        if (!response.ok) {
          throw new Error(`HTTPエラー!ステータス: ${response.status}`);
        }
        const json = await response.json();
        setData(json);
        setLoading(false);
      } catch (error) {
        setError(error);
        setLoading(false);
      }
    }

    fetchData();
  }, [url]);

  return { data, loading, error };
}

export default useFetch;

使用法:

import React from 'react';
import useFetch from './useFetch';

function MyComponent() {
  const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/todos/1');

  if (loading) return 

読み込み中...

; if (error) return

エラー: {error.message}

; return (

タイトル: {data.title}

完了: {data.completed ? 'はい' : 'いいえ'}

); } export default MyComponent;

カスタムフックのベストプラクティス

カスタムフックが効果的で保守可能であることを保証するために、以下のベストプラクティスに従ってください。

避けるべき一般的な落とし穴

高度なパターン

カスタムフックの構成

カスタムフックは、より複雑なロジックを作成するために組み合わせることができます。たとえば、useLocalStorageフックとuseFetchフックを組み合わせて、フェッチしたデータを自動的にローカルストレージに永続化することができます。

フック間のロジック共有

複数のカスタムフックが共通のロジックを共有している場合、そのロジックを別のユーティリティ関数に抽出し、両方のフックで再利用できます。

カスタムフックでのContextの使用

カスタムフックは、React Contextと組み合わせて使用し、グローバルな状態にアクセスしたり更新したりすることができます。これにより、アプリケーションのグローバルな状態を認識し、対話できる再利用可能なコンポーネントを作成できます。

実世界での例

カスタムフックが実世界のアプリケーションでどのように使用できるかの例をいくつか紹介します。

例:地図や配送サービスのような異文化アプリケーション向けのuseGeolocationフック

import { useState, useEffect } from 'react';

function useGeolocation() {
  const [location, setLocation] = useState({
    latitude: null,
    longitude: null,
    error: null,
  });

  useEffect(() => {
    if (!navigator.geolocation) {
      setLocation({
        latitude: null,
        longitude: null,
        error: 'このブラウザはジオロケーションをサポートしていません。',
      });
      return;
    }

    const watchId = navigator.geolocation.watchPosition(
      (position) => {
        setLocation({
          latitude: position.coords.latitude,
          longitude: position.coords.longitude,
          error: null,
        });
      },
      (error) => {
        setLocation({
          latitude: null,
          longitude: null,
          error: error.message,
        });
      }
    );

    return () => navigator.geolocation.clearWatch(watchId);
  }, []);

  return location;
}

export default useGeolocation;

結論

カスタムフックは、よりクリーンで、再利用性が高く、保守性の高いReactコードを書くための強力なツールです。複雑なロジックをカスタムフックにカプセル化することで、コンポーネントを簡素化し、コードの重複を減らし、アプリケーションの全体的な構造を改善できます。カスタムフックを活用し、より堅牢でスケーラブルなReactアプリケーションを構築する可能性を解き放ちましょう。

まず、既存のコードベースでロジックが複数のコンポーネント間で繰り返されている領域を特定することから始めてください。次に、そのロジックをカスタムフックにリファクタリングします。時間をかけて、開発プロセスを加速させ、コードの品質を向上させる再利用可能なフックのライブラリを構築できるでしょう。

ベストプラクティスに従い、一般的な落とし穴を避け、高度なパターンを探求して、カスタムフックを最大限に活用することを忘れないでください。練習と経験を積むことで、あなたはカスタムフックの達人となり、より効果的なReact開発者になるでしょう。