ReactHooksの基本・使い方・最適化を徹底解説【初心者向け】
この記事のポイント
React Hooksは関数コンポーネントで状態管理や副作用を扱う機能であり、useStateやuseEffectの適切な運用、メモ化による最適化、カスタムフックでのロジック再利用を通じて無限ループ等のアンチパターンを回避し、保守性の高い開発を実現する仕組みです。
React Hooksの各機能について正しい使い方が分からず、レンダリングの仕組みを根本から理解してモダンな開発スキルを身につけたいと考えていませんか。
こうした疑問にお答えします。
本記事の内容
- 主要なReact Hooksの基本概念と具体的な活用方法
- パフォーマンス最適化とカスタムフックの作成手順
- 無限ループなどのアンチパターンと回避策
React Hooksを正しく理解すれば、複雑なコンポーネントの状態管理や副次的な処理を驚くほどシンプルに記述可能です。
保守性の高いコードを書くための実践的なノウハウを凝縮しました。ぜひ最後までご覧ください。
React Hooksの基本的な仕組み
React Hooksは、Reactとは何かを踏まえた上で活用する、モダンなReact開発において欠かせない中心的な技術です。Hooksを正しく理解して活用すれば、コードの可読性や再利用性を劇的に向上させることができます。
ここでは、React Hooksの概要からクラスコンポーネントとの違い、守るべきルールについて詳しく解説します。
概要
React Hooksは2019年にリリースされたReact 16.8で導入された新機能です。関数コンポーネントの中でステート管理やライフサイクルなどの機能を「接続」するための仕組みを指します。
Hooks導入の最大の理由は、関数コンポーネントでもクラスコンポーネント以上に高度な機能をシンプルに扱うためです。複雑になりがちだったロジックを簡潔に記述できる点が大きなメリットといえます。
React Hooksには後方互換性があるため、既存プロジェクトへ段階的に導入可能です。現在の公式ドキュメントでは、Hooksを用いた開発が強く推奨されています。
クラスコンポーネントからの進化
Hooksの登場により、Reactの開発手法はクラスベースから関数ベースへと大きく進化し、Reactコンポーネント設計 の考え方そのものが刷新されました。以前はロジックの再利用に複雑なパターンが必要でしたが、Hooksによって大幅に簡略化されています。
クラスコンポーネントとReact Hooksを活用した関数コンポーネントの主な違いは以下の通りです。
| 比較項目 | クラスコンポーネント | Hooks(関数コンポーネント) |
|---|---|---|
| 状態管理 | this.state と setState を使用 | useState や useReducer を使用 |
| ライフサイクル | componentDidMount などのメソッド | useEffect で一括管理 |
| ロジックの共有 | HOCやRender Propsが必要 | カスタムフックで簡単に抽出 |
| コードの記述量 | 冗長になりやすい | 簡潔に記述可能 |
| thisの制約 | this のバインドを意識する必要がある | this を使用しないため直感的 |
Hooksを活用すれば、コンポーネント間でのロジックの再利用が容易になります。その結果、メンテナンス性の高いコードが実現できるでしょう。
実装時のルール
React Hooksを正しく動作させるためには、定められたルールを厳守する必要があります。ルールに反すると、状態の不整合や無限ループなどの予期せぬエラーを招くかもしれません。
主なルールは以下の2点です。
- フックを呼び出すのはトップレベルのみにする
- フックを呼び出すのはReactの関数内のみにする
ループや条件分岐、ネストされた関数の中でHooksを呼び出すのは避けましょう。順序を守ることで、Reactは各レンダー間のステートを正しく保持します。
また、useEffectを使用する際は依存配列に注意が必要です。静的解析ツールの指示に従い、古い値を参照し続けるエラーを確実に防ぎましょう。
機能の選択フローチャート
Reactには多くの標準Hooksが用意されています。実現したい機能に応じて適切なものを選ぶことが、パフォーマンスと保守性を両立させる鍵です。
主要なHooksの選択基準をまとめました。
- 状態を扱いたい場合
- 単純な値やオブジェクトの管理には「useState」
- 複雑な状態遷移やロジックが伴う管理には「useReducer」
- 副作用を実行したい場合
- API通信やDOM操作などの処理を行いたいなら「useEffect」
- 情報を保持しつつ再レンダリングを避けたい場合
- DOM要素の参照や変数の保持には「useRef」
- コンポーネント間でデータを共有したい場合
- propsのバケツリレーを避けたいときは「useContext」
- パフォーマンスを最適化したい場合
- 計算コストの高い値をメモ化するなら「useMemo」
- 関数の再生成を抑えて再描画を防ぐなら「useCallback」
これらを適切に組み合わせることで、スケーラブルなアプリケーションを構築できます。まずはuseStateとuseEffectの基本から習得し、徐々にステップアップしましょう。
代表的なReact Hooks
React Hooksは、関数コンポーネントで状態管理やライフサイクル機能を利用するための仕組みです。これまではクラスコンポーネントが必要だった複雑なロジックを、Hooksなら簡潔かつ再利用可能な形で記述できます。
モダンなReact開発に欠かせない主要な機能について、それぞれの役割と具体的な使い方を詳しく解説します。
状態を管理するuseState
useState は、関数コンポーネントに状態を持たせるための最も基本的なAPIです。Reactコンポーネントはユーザーの入力に合わせて表示を変える必要があり、このフックで値を保持します。
保持した値が更新されると、Reactは自動で画面を再レンダリングします。基本的な構文と構成要素は以下の通りです。
const [state, setState] = useState(initialValue);
- state:現在の状態の値
- setState:状態を更新するための関数
- initialValue:状態の初期値
カウンタの実装を例に挙げると、以下のようなコードになります。
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>現在のカウント: {count}</p>
<button onClick={() => setCount(count + 1)}>加算</button>
</div>
);
}
setStateを呼び出して状態の変化を伝えることで、安全にUIを更新できる点が大きな利点です。
副作用を制御するuseEffect
useEffectは、コンポーネントのレンダリングに伴って発生する副作用を管理します。クラス時代の各メソッドに代わり、ReactライフサイクルとuseEffect で全フェーズを一括して扱える点が大きな進化です。副作用には、APIによるデータ取得やDOMの直接操作、タイマーの設定などが含まれます。
このフックを活用すれば、特定のタイミングに限定して処理を実行できます。動作の詳細は、第2引数に渡す依存配列によって決まる仕組みです。
| 依存配列の指定 | 実行されるタイミング |
|---|---|
| 指定なし | 毎回のレンダリング後に実行 |
| 空の配列([]) | 初回マウント時のみ実行 |
| 値が入った配列 | 初回および指定した値の変化時に実行 |
以下に、APIからデータを取得する際の典型的な実装例を示します。
useEffect(() => {
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
setData(data);
};
fetchData();
}, []);
依存配列の設定を誤ると、無限ループや古い値の参照が発生するため注意が必要です。
値を共有するuseContext
useContextを使えば、コンポーネントツリー全体で特定の値を簡単に共有できます。通常はpropsでデータを渡しますが、階層が深いとバケツリレーが発生してコードが複雑化するため、React Contextの活用 を検討するべき場面です。
この問題を解決するのがContext機能であり、階層を飛び越えて必要な場所で直接値を取得可能です。実装は次の3ステップで完結します。
- createContextでコンテキストを作成
- Providerで共有したい値を囲む
- useContextで値を読み出す
const ThemeContext = React.createContext('light');
function App() {
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar() {
const theme = useContext(ThemeContext);
return <div>現在のテーマ: {theme}</div>;
}
ユーザー情報やテーマ設定の管理が容易になり、コードの可読性が大きく向上します。
DOMを参照するuseRef
useRefは、DOM要素への直接的な参照や、レンダリング間で共通の値を保持するために利用します。useStateとは異なり、値を更新しても再レンダリングが発生しない特徴があります。
画面表示には影響しない情報を管理する場合や、スクロール位置の制御に最適です。返されるオブジェクトのcurrentプロパティに、実際の値が格納されます。
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>入力欄にフォーカス</button>
</>
);
}
多用は避けるべきですが、どうしても命令的な操作が必要な場面で非常に強力なツールとなります。
パフォーマンスを最適化するReact Hooks
Reactのコンポーネントは、stateやpropsが変更されるたびに再レンダリングが行われます。この仕組みはReactの根幹ですが、大規模なアプリでは不要な再描画が重なり、動作の遅延を招くかもしれません。
パフォーマンスの問題を解決するため、Reactはメモ化というキャッシュの仕組みを利用したReact Hooksを提供しています。これらを正しく使い分けることで、アプリケーションの応答性を飛躍的に高められるでしょう。
計算結果を記憶するuseMemo
useMemoの使い方 を理解すれば、コストの高い計算結果を記憶し、必要なときだけ再計算を行うためのHookとして活用できます。レンダリングのたびに重い処理が実行されると、アプリのパフォーマンスが低下します。
useMemoは、第2引数に指定した依存配列の値が変化しない限り、前回の結果を再利用します。具体的な利用シーンは以下の通りです。
- 大量データのフィルタリングやソート
- 複雑な数学的計算
- 参照透過性を維持するためのオブジェクト生成
商品リストから完売商品を抽出する処理などは、リスト自体に変更がなければ再計算する必要はありません。useMemoを用いることで、無駄なリソース消費を効率的に抑えられます。
関数の再生成を防ぐuseCallback
useCallbackは、関数自体の定義をメモ化し、レンダリングごとの関数再生成を防ぐHookです。JavaScriptにおいて関数はオブジェクトの一種であり、定義のたびに新しいメモリ番地が割り当てられます。
関数をpropsとして渡すと、子コンポーネントは内容が同じでも新しい値が届いたと判断し、再レンダリングを起こします。useMemoとuseCallbackはどちらもメモ化を行いますが、対象と用途が異なります。
- useMemo:関数の実行結果(値)をメモ化する。高負荷な計算の繰り返しを避けるために使用する。
- useCallback:関数そのものをメモ化する。React.memoと組み合わせて子コンポーネントの不要な再描画を防ぐために使用する。
React.memoでラップした子コンポーネントを扱う際は、useCallbackをセットで活用するのがモダンな開発の推奨スタイルです。関数の再生成を防がないと、メモ化の効果が失われるので注意しましょう。
最適化が不要なケースの基準
全ての箇所でReact Hooksを使えば良いわけではありません。過剰な最適化は、かえってコードの可読性を下げ、メモリを無駄に消費する原因となります。
最適化を行うべきかどうかの判断基準を以下にまとめました。
- レンダリング速度が50msから100ms以上かかっているか
- 処理が単純な文字列結合や小さな配列操作ではないか
- 子コンポーネントが十分に軽量ではないか
Reactの標準的な挙動は非常に高速です。まずは最適化なしで実装し、ボトルネックが顕在化した段階で導入するのがベストプラクティスといえます。
コンパイラ導入による仕様変更の予測
React開発チームは、手動で行ってきた最適化を自動化する取り組みを進めています。React Compiler(React Forget)と呼ばれる新技術が注目を集めています。
これが完全に導入されると、開発者が明示的にHooksを書かなくても、ビルド時に自動でメモ化を適用できるようになります。今後の予測される変化は以下の通りです。
- useMemoやuseCallbackを記述する手間の削減
- パフォーマンスのための複雑な依存関係管理の解消
- 依存配列の管理ミスによるバグの減少
既存のReact Hooksがすぐに廃止されるわけではありません。しかし、将来的にはコンパイラによる最適化が主流になる流れを理解しておくことが重要です。
React Hooksのカスタムフック作成手順
Reactの開発において、コンポーネント内のロジックを再利用可能にする仕組みがカスタムフックです。これらを活用すれば、コードの肥大化を防ぎ、保守性の高いアプリケーションを構築できます。
カスタムフックを作成する際は、React Hooksの標準機能であるuseStateやuseEffectを組み合わせ、特定の機能をカプセル化します。ここでは、具体的な作成手順を4つのステップに分けて解説します。
①:対象のロジックをコンポーネントから抽出する
まずは、複数のコンポーネントで共通して使われているロジックや、複雑になりすぎた処理を特定して抽出します。特定の機能を外部に切り出すことで、コンポーネント自体はUIの表示に専念できる仕組みです。
抽出によって得られる主なメリットは3点あります。
- コードの再利用性:同じロジックを複数のコンポーネント間で共有できる
- 可読性の向上:コンポーネント内の記述が減り、構造が把握しやすくなる
- テストの容易性:ロジック単体を独立してテストしやすくなる
重複するロジックを切り出し、一つのフックとして独立させることが最初のステップとなります。
②:先頭にuseをつけて関数を定義する
抽出したロジックを実装する際は、関数名をuseから始める必要があります。これはReact Hooksの重要なルールであり、命名が適切でないとuseStateなどの標準フックが正しく認識されません。
- 正:
useUserData - 誤:
getUserData(Reactがフックとして認識しない可能性あり)
この命名規則を守ることで、リンターがフックの違反を検知できるようになります。useの直後の文字は大文字にするのが一般的な慣例です。
③:関数に必要な引数を設定する
カスタムフックを汎用的にするため、外部から動的に値を受け取れるよう引数を設定します。固定の値を扱うのではなく、引数でパラメータを渡せば、異なる条件下でも同じフックを使い回せるでしょう。
- フック内で必要な変数を引数として定義する
- 受け取った引数を内部の依存配列や計算に利用する
- コンポーネントで必要となる値や関数を戻り値としてreturnする
これにより、特定のデータに依存しない柔軟なカスタムフックが完成します。
④:作成したロジックをコンポーネント内で呼び出す
最後に、作成したフックを関数コンポーネント内で呼び出し、値を適用します。呼び出し方は、useStateなどの標準的なReact Hooksと全く同じです。
- カスタムフックから返された値などを分割代入で受け取る
- 受け取った値をJSXの中で反映し、イベントハンドラに紐付ける
フックは必ず関数コンポーネントのトップレベルで呼び出すことが必須です。以上の手順を踏むことで、ロジックがクリーンに整理され、拡張性の高い開発を実現できます。
React Hooksの実装で陥りやすいアンチパターン
React Hooksは関数コンポーネントで状態管理やライフサイクルを扱う強力なツールです。特性を正しく理解していないと、意図しないバグを引き起こす可能性があります。
レンダリングの仕組みやクロージャの性質に起因するミスは、経験豊富な開発者でも注意が必要です。開発現場で頻出する4つのアンチパターンと、その解決策を詳しく紹介します。
依存配列の指定漏れによる無限ループ
useEffectなどのReact Hooksでは、第二引数の依存配列の扱いが重要です。指定すべき値を省略したり不適切な値を設定したりすると、再レンダリングが止まらない無限ループが発生します。
無限ループが発生する主な原因をまとめました。
- stateの更新と参照の循環。useEffect内で更新した値をそのまま依存配列に入れているケースです。
- オブジェクトや配列の参照。レンダリングのたびに新しく生成される値を含めると、別物と判定され実行が繰り返されます。
- 関数の再生成。コンポーネント内で定義した関数をそのまま配列に入れると、毎回のレンダリングで原因となります。
これらの問題を回避するには、以下の対策が有効です。
- eslint-plugin-react-hooksを導入し、依存関係の漏れを自動でチェックする。
- 関数を依存配列に含める場合は、useCallbackでメモ化を行う。
- setStateを呼ぶ際、関数型アップデートを利用して依存関係を外す。
条件分岐内での呼び出し
React Hooksには、フックを呼び出すのはトップレベルのみという厳格なルールが存在します。if文などの条件分岐やループ、ネストされた関数の中で呼び出してはいけません。
Reactはフックが呼び出される順番に基づいて、各状態を内部的に管理しています。条件によって実行順序が変わると、状態の整合性が保てずアプリのクラッシュや予期せぬ挙動を招く恐れがあります。
| 項目 | 正しい書き方 | 誤った書き方(アンチパターン) |
|---|---|---|
| 呼び出し場所 | コンポーネントの最上位 | if文やfor文などのブロック内部 |
| 実行順序 | 常に同じ順番で実行 | 条件によって実行順が変わる |
| 対処法 | フック内部で条件判定を行う | 条件分岐の後にフックを記述する |
特定の条件下でロジックを動かしたい場合は、フック自体の呼び出しを制御してはいけません。useEffectなどの内部に条件分岐を記述し、実行内容を管理してください。
古い状態を参照するクロージャの問題
JavaScriptのクロージャという性質により、非同期処理やイベントリスナー内で古い値(Stale Closures)を参照することがあります。その時点のstateが固定され、最新の状態が反映されない現象です。
例えば、ボタンを押して3秒後にcountを表示する処理では、待機中に値が更新されても古い値を表示してしまいます。コールバック関数が実行時の値をキャプチャしているためです。
この問題を解決するには以下の方法を試してください。
- useRefを使用する。refで保持する値は常に最新の状態を指し、レンダリングをトリガーせずに参照できます。
- クリーンアップ関数を活用する。useEffect内で古いタイマーやリスナーを破棄し、最新の依存関係で再登録を行う手法です。
コンポーネントが描画されるたびに、その時点のスナップショットが生成される仕組みを意識しましょう。論理的なコードを維持するためには非常に重要な考え方です。
サーバーサイドでの呼び出しエラー
Next.jsなどのフレームワークを使う際は、React Hooksの呼び出し場所に注意が必要です。特にuseEffectはクライアントサイドでのみ動作する設計となっています。
サーバーサイドでフックを扱う際の主な注意点です。
- ブラウザ専用APIへのアクセス。windowやdocumentをuseEffectの外で参照すると、サーバー側に存在しないためエラーです。
- 実行タイミングの不一致。サーバー側は一度の実行でHTMLを作りますが、エフェクトはブラウザ上でのみ動きます。
基本的には、クライアント限定の処理はuseEffectの中に記述してください。サーバー側のデータが必要な場合は専用の関数を利用し、役割を明確に分けることが推奨されます。
まとめ:React Hooksで複雑な状態管理をシンプルにする
React Hooksの基本的な仕組みやuseState、useEffectといった代表的な機能の使い方、さらにパフォーマンスの最適化まで詳しく解説しました。フックを正しく理解することで、無限ループなどのアンチパターンを回避し、堅牢なアプリケーション開発が可能になります。
クラスコンポーネントから進化したReact Hooksは、モダンな開発に欠かせません。依存配列の指定などの注意点を押さえ、効率的なコーディングを目指しましょう。
本記事のポイント
- useStateやuseEffectを活用し、関数のライフサイクル内で状態と副作用を適切に管理する
- useMemoやuseCallbackを使い、再レンダリングによるパフォーマンス低下を抑制する
- 独自のロジックをカスタムフックとして切り出し、コードの可読性と再利用性を高める
React Hooksをマスターすれば、複雑なコンポーネント設計も驚くほどシンプルに変化します。コードの保守性を高め、エラーの少ないスムーズな開発体験を手に入れましょう。
モダンな開発現場で即戦力として活躍できるスキルが身につくはずです。具体的な実装方法やフロントエンドの最適化について詳しく知りたい方は、当サイトの関連記事も併せてご覧ください。
開発支援や技術コンサルティングに関するご相談も随時受け付けています。お困りの際はお気軽にご連絡ください。
React Hooksに関してよくある質問
参考文献
執筆者
編集部
Next.jsやAIを活用したモダンWeb開発・SEO実装に関する情報を発信。SEOに最適化したモダンWebサイト制作、設計ノウハウ、構造化データや内部リンク設計などを中心に扱っています。
監修者
MT Templates 代表/編集長
海外メディア企業でSEOエディターとして従事後、独立。複数メディア運営の経験をもとに、Next.jsやAIを活用したWeb開発・SEO技術を発信。リード獲得につながるサイト構築からSEO設計まで一貫したサポートを提供している。
関連記事
Reactのライフサイクルの仕組みとuseEffectでの実装【図解】
旧機能の廃止や再描画に悩む方へ、Reactのライフサイクルを図解し、useEffect等のフックによるアンマウント制御を学ぶことで、最適な実装が可能です。
Reactのコンポーネントの作り方・分け方・設計【初心者向け】
Reactのコンポーネントの適切な分け方や作り方に悩む方へ、種類や使い方、設計、ライブラリまで解説し、実務で活きる高保守性コード習得を導きます。
ReactのUIライブラリ人気7選・要件別の徹底比較【プロ解説】
UI開発に悩む方へ、人気のReactのUIライブラリを解説し、Material UI等の活用で技術的負債を防ぎ、美しいUIデザインによる保守性の高い開発を実現します。
useMemoの使い方・使わない基準とは?useCallbackとの違い
ReactでuseMemoの用途にお悩みですか。useCallbackやuseEffectとの違い、使わない基準を解説。不要な再レンダリングを防ぎ、アプリを最適化できます。
ReactとRedux入門・Toolkitの全5つの実装手順【初心者向け】
ReactでReduxを導入したい方向けに、ToolkitやTypeScriptでの実装手順から使わない条件まで解説し、実務的な状態管理スキルが身につく入門記事です。
ReactのContextの使い方とアンチパターン【プロが徹底解説】
ReactのContextでPropsバケツリレーを解消する使い方を解説。再レンダリングのアンチパターンやReduxとの比較を通じ、保守性の高い実装が可能です。