Reactのフックは、関数コンポーネント内で状態やライフサイクルなどのReactの機能を使うためのものです。
useState
やuseEffect
はその最も基本的なフックですが、これらを組み合わせて、特定の目的に合わせた「カスタムフック」を作成することができます。
今回は、ローカルストレージを操作するカスタムフックの作成と使用方法について解説します。
カスタムフックの作成: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
は、依存配列にkey
とvalue
を指定することで、これらの値が変更されたときにのみ実行されるようになっています。これにより、不要な書き込みを防ぎ、効率的なデータ管理を実現しています。
カスタムフックの使用例
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からデータを非同期に取得します。取得中、完了後、エラー発生時の状態をそれぞれloading
、data
、error
として管理します。これにより、データ取得のロジックをコンポーネントから分離し、簡単に再利用することができます。
使用例
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のコミュニティによって多く共有されており、自分で一から作成する必要はなく、既存のカスタムフックを利用することもできます。