本文へスキップ
ウェブエンジニア問題集
第10章

Hooksの使い分け — 判断フローと横断比較

約8分
この章の目次開く

ここまで7つのフックを個別に学んできました。 この章では全体を俯瞰して、「この場面ではどのフックを使うべきか」を迷わず判断できるようにします。

学習者学習者

フックが7つも出てきて頭がごちゃごちゃ…。結局どの場面でどれを使えばいいの?


7つのフックの横断比較

各フックの性質を整理します。

useState — UIに影響する値を管理する。値を変更すると再レンダーが走り、画面が更新される。

useContext — Providerがサブツリーへ提供した値を読み取る。テーマやログインユーザーなど、離れた複数のコンポーネントで共有する値に使う。

useRef — UIに影響しない値を保持する。DOM参照、タイマーID、前回の値など。変更しても再レンダーは起きない。

useEffect — レンダー後に外部と同期する。API呼び出し、購読、タイマーなど。クリーンアップ関数で後始末ができる。

useMemo — 重い計算結果をキャッシュする。依存が変わらない限り前回の結果を返す。レンダー中に同期実行。

useCallback — 関数オブジェクトをキャッシュする。useMemoの関数特化版。React.memoされた子コンポーネントへのpropsや、useEffectの依存配列で参照を安定させたいときに使う。

useTransition — state更新の一部を緊急ではない更新としてマークする。入力操作などを優先し、重い一覧やタブ内容のレンダーを中断可能にする。


判断フローチャート

「値を持ちたい」「値を共有したい」「処理を実行したい」「パフォーマンスを改善したい」という4つの出発点から、どのフックに辿り着くかを示します。

値を持ちたい場合

値を共有したい場合

処理を実行したい場合

子コンポーネントへの受け渡しを最適化したい場合

重い画面更新でも操作を止めたくない場合


よくある疑問

フック全体を振り返って整理するイメージ

useStateとuseContextはどう使い分ける?

useState は値を保持して更新するフックです。useContext は、上位のProviderが持つ値を離れたコンポーネントから読み取るフックです。

値を実際に所有するのは useState を呼び出したコンポーネントで、Contextはその値を配下へ届けます。ローカルな値なら useState だけで十分です。サブツリー内の離れた複数箇所で同じ値を使う必要が出たときに、Contextとの組み合わせを検討します。

useMemoとuseCallbackはどう使い分ける?

メモ化したい対象が「値」なら useMemo、「関数」なら useCallback です。useCallback は useMemo の関数特化版なので、useMemo(() => () => { ... }, [deps]) と書けば useCallback と同じことができますが、意図の明確さから関数には useCallback を使います。

useRefとuseMemoの違いがわからない

useRef は自分で .current を書き換えるまでずっと同じ値を持ち続けます。useMemo は依存配列が変わったら自動で再計算します。

「タイマーIDを保持する」→ 自動再計算は不要 → useRef。 「ユーザーリストをソートした結果を保持する」→ 元データが変わったら再計算 → useMemo。

useEffectとuseMemoは両方とも依存配列があるけど何が違う?

実行タイミングが異なります。useMemo はレンダー中に同期的に実行され、すぐに値を返します。useEffect はレンダーが完了してDOMに反映された後に実行されます。

「ソートした結果を今すぐJSXで使いたい」→ useMemo。 「APIからデータを取得したい」→ useEffect。

useCallbackを使っているのにパフォーマンスが改善しない

学習者学習者

useCallback を使ってるのに全然速くならない…。何が足りないの?

先生先生

受け取る側の子コンポーネントが React.memo でラップされているか確認しよう。useCallback 単体ではパフォーマンスに影響しない。参照の安定性を「活かす側」がいて初めて効果があるよ。

受け取る側の子コンポーネントが React.memo でラップされていない可能性が高いです。useCallback 単体では効果がなく、参照の安定性を活用する側(React.memo、useEffect の依存配列)があって初めて意味を持ちます。

useMemoとuseTransitionはどう使い分ける?

useMemo は、同じ依存値に対する計算結果を再利用して、計算のやり直しを減らします。useTransition は1回分の処理自体を軽くせず、後回しにできる画面更新の優先度を下げます。

「同じ条件の重い計算を繰り返している」→ useMemo。 「重い一覧の更新中も入力やクリックへ反応したい」→ useTransition。

どちらも必要な場合はありますが、目的は別です。まず遅い場所を計測し、計算の重複を減らしたいのか、操作の応答性を保ちたいのかで判断します。


カスタムフックへの導線

7つのフックを使う処理が複数のコンポーネントで重複するようになったら、カスタムフックとして切り出せます。

// useWindowSize — ウィンドウサイズを監視するカスタムフック
function useWindowSize() {
  const [size, setSize] = useState({ width: 0, height: 0 });
 
  useEffect(() => {
    const handleResize = () => {
      setSize({ width: window.innerWidth, height: window.innerHeight });
    };
    handleResize(); // 初期値を設定
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);
 
  return size;
}
 
// 使う側
function Header() {
  const { width } = useWindowSize();
  return <header>{width < 768 ? 'モバイル' : 'デスクトップ'}</header>;
}
tsx

カスタムフックは use で始まる関数で、中で他のフックを自由に使えます。「イベントリスナーの登録(useEffect)+ 値の管理(useState)」のような定型パターンを1つの関数にまとめられます。

カスタムフック自体は本書のスコープ外ですが、7つのフックを理解していれば、既存のカスタムフックのコードを読んだときに「ああ、useEffectで購読してuseStateで値を管理しているのか」と構造がわかるようになります。


まとめ

7つのフックはそれぞれ異なる役割を持っています。

useState はUIに影響する状態を管理し、useContext はProviderが提供した共有値を読み取ります。useRef はUIに影響しない値を再レンダーなしに保持します。useEffect はレンダー後に外部との同期処理を実行します。useMemo は重い計算結果をキャッシュし、useCallback は関数オブジェクトをキャッシュします。useTransition は後回しにできる更新の優先度を下げ、緊急な操作への応答を保ちます。

判断の起点は「その値は画面に映るか」「どの範囲で共有するか」「その処理はレンダーの結果として必要か」「参照の安定性を受け取る側が活用するか」「その更新は後回しにできるか」の5つです。

これでReactのHooksの土台は固まりました。この先Next.jsなどのフレームワークに進むと、Server ComponentやApp Routerなど新しい概念が出てきますが、それらの理解もここで学んだHooksの知識が基盤になります。

Reactクイズに挑戦する主要なReact Hooksの役割と使い分けを総復習クイズで確認しよう