Reactのカスタムフックを理解しよう

JavaScript
スポンサーリンク

Reactのフックは、関数コンポーネント内で状態やライフサイクルなどのReactの機能を使うためのものです。

useStateuseEffectはその最も基本的なフックですが、これらを組み合わせて、特定の目的に合わせた「カスタムフック」を作成することができます。

今回は、ローカルストレージを操作するカスタムフックの作成と使用方法について解説します。

カスタムフックの作成:useLocalStorage

import { useEffect, useState } from "react";

const useLocalStorage = (key, initialValue) => {
  const [value, setValue] = useState(() => {
    const jsonValue = window.localStorage.getItem(key);
    if (jsonValue != null) return JSON.parse(jsonValue);
    return initialValue;
  });

  useEffect(() => {
    window.localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);

  return [value, setValue];
};

export default useLocalStorage;

このカスタムフックは、ローカルストレージにデータを保存し、そのデータを状態として管理するために使います。

useStateを使って状態を宣言し、useEffectを使ってその状態が更新されたときにローカルストレージにもその値を保存します。

useStateの工夫

useStateの初期値に関数を渡すことで、この関数はコンポーネントの初回レンダリング時に一度だけ実行されます。これにより、不要な計算を避けることができ、パフォーマンスを向上させることができます。

useEffectの役割

useEffectは、依存配列にkeyvalueを指定することで、これらの値が変更されたときにのみ実行されるようになっています。これにより、不要な書き込みを防ぎ、効率的なデータ管理を実現しています。

カスタムフックの使用例

import useLocalStorage from "./useLocalStorage";

function App() {
  const [number, setNumber] = useLocalStorage("number", 0);

  return (
    <>
      <h1>カスタムフック:好きな数字を登録</h1>
      <p>{number}</p>
      <button onClick={() => setNumber(10)}>好きな数字を登録</button>
    </>
  );
}

このコンポーネントでは、useLocalStorageカスタムフックを使用して、ユーザーの好きな数字をローカルストレージに保存しています。ボタンをクリックすると、setNumberを通じて数字が更新され、新しい値がローカルストレージに保存されます。

ユーザー体験の向上

このカスタムフックを使用することで、ユーザーがブラウザを閉じても、好きな数字が保存されるため、次回ブラウザを開いたときにもその値を保持することができます。これにより、ユーザー体験が向上します。

再利用性の向上

useLocalStorageは汎用的なカスタムフックであり、どのコンポーネントでもローカルストレージを簡単に操作するために再利用することができます。

より実用的なカスタムフックの例

カスタムフックは、特定の機能を抽象化し、再利用可能なロジックを作成するための強力なツールです。以下に、2つの具体的なカスタムフックの例とその解説を示します。

1. ウィンドウの幅に応じたレスポンシブデザインを実現するカスタムフック

import { useState, useEffect } from "react";

const useWindowWidth = () => {
  const [windowWidth, setWindowWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWindowWidth(window.innerWidth);
    window.addEventListener("resize", handleResize);

    // クリーンアップ関数
    return () => window.removeEventListener("resize", handleResize);
  }, []);

  return windowWidth;
};

export default useWindowWidth;

解説

このカスタムフックuseWindowWidthは、ウィンドウの幅を状態として追跡します。useEffect内でリサイズイベントリスナーを設定し、ウィンドウの幅が変更されるたびに状態を更新します。このカスタムフックを使用することで、コンポーネントはウィンドウの幅に応じて動的にレンダリングを変更することができます。

使用例

function ResponsiveComponent() {
  const windowWidth = useWindowWidth();

  return (
    <div>
      <h1>ウィンドウの幅に応じたレンダリング</h1>
      <p>現在のウィンドウ幅は {windowWidth}px です。</p>
    </div>
  );
}

2. APIからデータを取得し、ローディングとエラー状態を管理するカスタムフック

import { useState, useEffect } from "react";

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

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
};

export default useFetch;

解説

useFetchカスタムフックは、指定されたURLからデータを非同期に取得します。取得中、完了後、エラー発生時の状態をそれぞれloadingdataerrorとして管理します。これにより、データ取得のロジックをコンポーネントから分離し、簡単に再利用することができます。

使用例

function DataFetchingComponent() {
  const { data, loading, error } = useFetch("https://api.example.com/data");

  if (loading) return <p>ローディング中...</p>;
  if (error) return <p>エラーが発生しました: {error.message}</p>;

  return (
    <div>
      <h1>取得したデータ</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
}

これらのカスタムフックは、Reactアプリケーションの開発において、コードの再利用性を高め、複雑さを減らすために非常に役立ちます。useWindowWidthはレスポンシブデザインの実装を、useFetchはデータ取得のロジックを簡素化し、それぞれのコンポーネントがより宣言的になるように支援します。

おわり

カスタムフックを作成することで、コンポーネント間での状態管理のロジックを共有し、コードの重複を減らし、メンテナンス性を高めることができます。

また、カスタムフックはReactのコミュニティによって多く共有されており、自分で一から作成する必要はなく、既存のカスタムフックを利用することもできます。