Reactのカスタムフックの実践例まとめ!便利スニペット集で学ぶ
生徒
「先生、カスタムフックって便利そうだけど、どんな例がありますか?」
先生
「いくつかの実践的なスニペットを使うと、すぐに役立つフックを作れますよ。」
生徒
「具体的にどんなスニペットですか?」
先生
「それでは、簡単な例を順番に紹介します!」
1. ローカルストレージと同期するカスタムフック
useStateとuseEffectを組み合わせて、データをブラウザのローカルストレージに保存するフックです。ページをリロードしても値が保持されます。
import { useState, useEffect } from "react";
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
export default useLocalStorage;
2. ウィンドウサイズを取得するフック
ブラウザの幅や高さをリアルタイムで取得できるカスタムフックです。レスポンシブ対応に便利です。
import { useState, useEffect } from "react";
function useWindowSize() {
const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight });
useEffect(() => {
const handleResize = () => setSize({ width: window.innerWidth, height: window.innerHeight });
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
return size;
}
export default useWindowSize;
3. フォーム入力をまとめて管理するフック
フォームの入力値をオブジェクトでまとめて管理できます。inputやtextareaのonChangeハンドラーに便利です。
import { useState } from "react";
function useForm(initialValues) {
const [values, setValues] = useState(initialValues);
const handleChange = (e) => {
const { name, value } = e.target;
setValues(prev => ({ ...prev, [name]: value }));
};
return [values, handleChange];
}
export default useForm;
4. APIデータを取得するフック
非同期で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(() => {
fetch(url)
.then(res => res.json())
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
}, [url]);
return { data, loading, error };
}
export default useFetch;
5. 前回の値を保持するフック
前回の値を保持して比較したいときに便利なカスタムフックです。値の変化を監視する際に役立ちます。
import { useRef, useEffect } from "react";
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
export default usePrevious;
6. カスタムフックで作業効率をアップ
今回紹介したスニペットを使うと、状態管理、フォーム操作、API通信、ウィンドウ操作などの共通処理を簡単に再利用できます。自分用の便利フック集を作ることで、React開発の効率が大幅にアップします。
まとめ
React開発において、カスタムフックを活用することは、単にコードを短くする以上の大きなメリットをもたらします。今回ご紹介した「ローカルストレージの同期」「ウィンドウサイズの監視」「フォーム管理」「API通信」「前回の値の保持」といったスニペットは、実務の現場でも頻繁に登場するロジックばかりです。これらを共通部品として切り出すことで、コンポーネント本体は「見た目(UI)」の構築に専念でき、プログラム全体の可読性とメンテナンス性が劇的に向上します。
カスタムフックを作成する際のポイントは、再利用性を意識しつつも、特定のビジネスロジックに依存しすぎない「汎用的なインターフェース」を設計することです。例えば、今回紹介した useFetch フックをさらに拡張して、認証トークンを自動で付与する機能や、リトライ処理を追加することも可能です。自分自身のプロジェクトに合わせたオリジナルのフック集を育てていくことは、開発者としての大きな資産になるでしょう。
応用編:複数のカスタムフックを組み合わせる実践テクニック
実際のアプリケーション開発では、一つのコンポーネント内で複数のカスタムフックを組み合わせて使用することが一般的です。例えば、ショッピングサイトの「ユーザー設定画面」を想定してみましょう。
以下のサンプルコードは、今回学んだ useForm と useLocalStorage を組み合わせて、フォームの入力内容をリアルタイムでブラウザに保存する高度な実装例です。これにより、ユーザーが誤ってページを閉じてしまっても、入力内容が消えない親切なUIを実現できます。
import React, { useEffect } from "react";
import useForm from "./hooks/useForm";
import useLocalStorage from "./hooks/useLocalStorage";
function UserProfileEditor() {
// ローカルストレージから初期データを取得
const [savedProfile, setSavedProfile] = useLocalStorage("user_profile", {
userName: "",
bio: ""
});
// フォームの状態管理にカスタムフックを利用
const [values, handleChange] = useForm(savedProfile);
// 入力値が変わるたびにストレージに保存する(自動保存機能)
useEffect(() => {
setSavedProfile(values);
}, [values, setSavedProfile]);
return (
<div className="p-4 border rounded shadow-sm">
<h3 className="mb-3">プロフィール編集</h3>
<div className="mb-3">
<label className="form-label">ユーザー名</label>
<input
type="text"
name="userName"
className="form-control"
value={values.userName}
onChange={handleChange}
/>
</div>
<div className="mb-3">
<label className="form-label">自己紹介</label>
<textarea
name="bio"
className="form-control"
value={values.bio}
onChange={handleChange}
/>
</div>
<div className="text-muted small">
※入力内容は自動的にブラウザへ保存されます。
</div>
</div>
);
}
export default UserProfileEditor;
最新のReact開発におけるカスタムフックの重要性
Next.js(App Router)の普及により、サーバーコンポーネントとクライアントコンポーネントの使い分けが重要視されるようになりました。カスタムフックは「クライアントサイドのロジック」をカプセル化するための強力な武器となります。ブラウザ特有のAPI(windowオブジェクトやlocalStorageなど)にアクセスする処理は、すべてカスタムフックの中に閉じ込めておくことで、コンポーネントの構造がすっきりとし、デバッグも格段に楽になります。
また、TypeScriptを導入しているプロジェクトであれば、カスタムフックの引数や戻り値に適切な型(InterfaceやType)を定義することで、型安全な開発が可能になります。例えば、useFetch<T> のようにジェネリクスを活用すれば、取得するデータの型を呼び出し側で指定でき、予測可能なプログラミングが実現できます。
これからReactをより深く学びたい方は、ライブラリが提供している標準フック(useState, useEffect, useContext, useMemo, useCallback)の挙動を正しく理解した上で、それらを組み合わせて「自分だけの便利な道具箱」を作ってみてください。コードの重複を減らし、宣言的な記述を心がけることで、プロフェッショナルなフロントエンドエンジニアへの道が開けるはずです。
生徒
「先生、まとめの記事を読んでカスタムフックの凄さがさらによく分かりました!特に複数のフックを組み合わせる方法は、実際のアプリ開発ですぐに使えそうですね。」
先生
「その通りです。一つひとつのフックはシンプルですが、それらをレゴブロックのように組み合わせることで、複雑な機能も驚くほど綺麗に実装できるのがReactの醍醐味なんですよ。」
生徒
「今まではコンポーネントの中にuseEffectやuseStateをたくさん書いてしまって、コードが長くなって困っていたんです。これからはカスタムフックに切り出して、再利用性を高めるように意識してみます。」
先生
「素晴らしい意気込みですね!特に useForm のようなフックは、一度作っておけばどんなプロジェクトでも使い回せます。最初は既存のスニペットを模倣するところから始めて、徐々に自分なりのアレンジを加えていくのが上達の近道ですよ。」
生徒
「TypeScriptでの型定義についても触れられていましたが、ジェネリクスを使うとさらに便利になりそうですね。次は、API通信のフックを型安全に実装することに挑戦してみたいと思います!」
先生
「いいですね。型安全なカスタムフックはチーム開発でも非常に喜ばれます。エラーの少ない、堅牢なアプリケーションを目指して頑張りましょう。もし実装で迷ったら、いつでも相談してくださいね。」
生徒
「はい!ありがとうございます。カスタムフックを活用して、もっとスマートなコードが書けるように練習を積み重ねます!」