React useContextの基本と実務での活用方法
はじめに
Reactはコンポーネント単位でUIを組み立てていくライブラリですが、複数のコンポーネント間で同じ状態を共有したい場面はよくあります。 そのような状況を効率的に解決する方法として、useContext というフックが用意されています。 このフックを活用すると、深い階層のコンポーネントにデータを渡す際の手間を少なくできるでしょう。 一方で、仕組みをしっかり理解していないと、どこでどのように使えばよいのか迷うこともあるかもしれません。 そこで、皆さんの疑問を解消しながら、実務でも使いやすい方法を紹介していきます。 とくにプログラミングに慣れていない方に向けて、できるだけ平易な言葉を使って解説します。 Reactを学び始めたばかりでも、考え方や使い方をイメージしやすくなるのではないでしょうか。
この記事を読むとわかること
useContext の基本的な役割とコンセプト。 実務でよくあるシーンにどう活かせるか。 具体的なコード例と、初心者でも理解しやすい解説。 注意すべきポイントや、使いすぎを防ぐための考え方。
これらを踏まえて学習していくと、Reactの状態管理について一段と理解が深まるでしょう。
useContextとは
useContext は、Reactで用意されているContextという仕組みを簡単に扱うためのフックです。 Reactでは、コンポーネントにプロパティ(props)を渡して情報を共有することが基本です。 しかし、コンポーネントの階層が深くなるほど「どこからどこまでpropsを渡したんだっけ」と管理が複雑になります。 これを一般的に「Prop Drilling(プロップの受け渡しが深い階層まで連鎖する問題)」と呼ぶこともあります。 useContextを使えば、このような煩雑さをある程度解消し、必要なコンポーネントだけがデータを受け取れるようにできます。 それによって、アプリ全体の可読性や保守性が向上しやすくなるでしょう。
useContextが生まれた背景
React自体は、コンポーネントの再利用性を高めるために設計されています。 しかし、コンポーネント同士の連携はprops経由が基本です。 複数のコンポーネントで使うデータを渡すとなると、propsの数が増えたり、親子の階層が深まったりして、コードが読みにくくなるケースがあります。 この課題を解決するために、ReactにはContext API という仕組みがあります。 Context APIはpropsを通さずに、コンポーネントツリー全体や一部に対して同じデータを共有できる仕組みです。 そこへフックの形で簡単にアクセスできるようにしたのがuseContext です。
実務でよくあるシーン
- テーマ切り替え(ライトモードやダークモードなど)の設定。
- ログインユーザー情報の保持。
- 設定画面から変更した言語をアプリ全体に反映する。
- 複数ページにまたがって同じデータを参照したい。
こうした場面では、コンポーネントごとに同じ情報を繰り返しpropsで渡すよりも、Contextにまとめてしまったほうが見通しを保ちやすいです。 とくに大規模なアプリほど、各所で共通のデータを使うことが多くなりがちです。 そこでuseContextをうまく活用できると、コードがシンプルになりやすいでしょう。
useContextの使い方
useContextを使うには、大きく分けて2つのステップが必要になります。 1つはコンテキストを作成し、アプリ全体または一部のコンポーネントをコンテキストでラップすること。 もう1つはuseContextフックを利用して、必要なデータにアクセスすることです。
コンテキストの定義とProvider
コンテキストを作るときは、Reactが用意している createContext
を使います。
このcreateContextで作ったオブジェクトにデータを持たせたい場合は、そのオブジェクトのProviderコンポーネントを利用します。
Providerコンポーネントは、まるで「この下にあるコンポーネントにデータを渡しますよ」と宣言するようなイメージです。
例えば以下のような流れでコンテキストを定義することが多いです。
import React, { createContext } from "react"; // 1. コンテキストを作成する export const UserContext = createContext(null); // 2. Providerコンポーネントを用意して、必要な値をラップする export function UserProvider({ children }) { const user = { name: "Taro", role: "admin", }; return ( <UserContext.Provider value={user}> {children} </UserContext.Provider> ); }
この例では、UserContextという名前のコンテキストを作成し、UserProviderというコンポーネントでラップしています。
value
に入れた user
オブジェクトを、ツリー下のコンポーネントは自由に取得できます。
こうしておけば、親から子へpropsを何段も渡すことなく、どこからでもユーザー情報を参照できるようになります。
useContextでデータを読み込む
アプリの中でUserProviderを使ってラップしたあと、実際にデータを呼び出す場合は以下のように記述します。
import React, { useContext } from "react"; import { UserContext } from "./UserContext"; function UserProfile() { const userInfo = useContext(UserContext); return ( <div> <p>名前: {userInfo.name}</p> <p>権限: {userInfo.role}</p> </div> ); } export default UserProfile;
ここで useContext(UserContext)
と書けば、UserProviderで value
に設定した user
オブジェクトがすぐに取り出せます。
ポイントは、プロパティを渡す手間が省けることと、深い階層でも同じ情報を参照できることです。
複数のコンポーネントが同じUserContextを参照すれば、それぞれが同じユーザー情報を表示できます。
useContextを使った具体的なコード例
もう少し踏み込んだ例として、テーマの切り替えを行うシナリオを考えてみましょう。 たとえばダークモードとライトモードをワンタッチで切り替えたい場合や、ユーザーが好みの配色テーマを選べるアプリなどです。
実務を意識したシナリオ
- 上部にヘッダーがあるアプリを想定し、そのヘッダーの背景色をコンテキストで管理する。
- 画面の一部にあるボタンを押すとテーマが切り替わり、ヘッダーや本文がそのテーマに合わせてスタイル変更される。
- propsを大量に渡さなくてもよいので、メンテナンス性を保ちやすい。
こういったイメージを持ちながら、コードでどう書くのかを見てみましょう。
コンポーネントの実装
まずは、テーマを定義するコンテキストとProviderを用意します。
ThemeContext.js
というファイルを用意して、次のように記述します。
import React, { createContext, useState } from "react"; export const ThemeContext = createContext(); export function ThemeProvider({ children }) { const [theme, setTheme] = useState("light"); const toggleTheme = () => { if (theme === "light") { setTheme("dark"); } else { setTheme("light"); } }; return ( <ThemeContext.Provider value={{ theme, toggleTheme }}> {children} </ThemeContext.Provider> ); }
ここでは theme
という文字列を状態として管理し、それを ThemeContext.Provider
で渡しています。
さらに toggleTheme
という関数を同時に提供することで、どのコンポーネントからでもテーマの切り替えができるようになります。
次に、アプリのルートとなる App.js
で ThemeProvider
を使い、ほかのコンポーネントをラップします。
import React from "react"; import { ThemeProvider } from "./ThemeContext"; import Header from "./Header"; import MainContent from "./MainContent"; function App() { return ( <ThemeProvider> <Header /> <MainContent /> </ThemeProvider> ); } export default App;
<Header>
と <MainContent>
は、テーマを参照したり、テーマを切り替えたりできるコンポーネントとして定義しておくと便利です。
たとえば Header.js
は以下のように書けます。
import React, { useContext } from "react"; import { ThemeContext } from "./ThemeContext"; function Header() { const { theme } = useContext(ThemeContext); const style = { backgroundColor: theme === "light" ? "#eee" : "#333", color: theme === "light" ? "#333" : "#eee", padding: "10px", }; return ( <header style={style}> <h1>ようこそ</h1> </header> ); } export default Header;
ここでは theme
の状態によって背景色や文字色を分けています。
同じように、メイン部分のコンポーネント MainContent.js
には、テーマを切り替えるためのボタンを配置する例を示します。
import React, { useContext } from "react"; import { ThemeContext } from "./ThemeContext"; function MainContent() { const { theme, toggleTheme } = useContext(ThemeContext); const style = { backgroundColor: theme === "light" ? "#fff" : "#555", color: theme === "light" ? "#555" : "#fff", minHeight: "200px", padding: "20px", }; return ( <main style={style}> <p>ここはメインコンテンツです。</p> <button onClick={toggleTheme}>テーマを切り替える</button> </main> ); } export default MainContent;
これで、ボタンをクリックするたびにテーマが切り替わり、ヘッダーや本文のスタイルが連動して変更されます。
propsをいちいち渡さなくても、useContext
で直接テーマと切り替え関数を取得できるため、コードがすっきり見えるのではないでしょうか。
useContextで気をつけたいポイント
useContextは便利ですが、使いどころを考えないと逆に状態管理が分散しすぎてしまうおそれがあります。 すべてをContextに詰め込むと、アプリ全体で扱うデータのつながりが見えにくくなるかもしれません。 コンポーネント同士のやりとりが増えすぎて混乱を招くなら、別の状態管理方法(たとえばReduxや他のフックなど)との比較検討も有効です。 そうは言っても、小~中規模のアプリで「単純に複数コンポーネント間の共有データが欲しい」といったシーンでは、useContextがシンプルに機能するでしょう。
アプリが大きくなる場合は、コンテキストごとに役割を明確に分けると読みやすくなります。
また、更新頻度が高いデータをContextで管理していると、再レンダリングが頻発してパフォーマンスが落ちるケースもあります。 そんなときは、Contextで管理すべきデータの範囲を必要最小限に絞るなど、設計段階で調整してみてください。
むやみに複数のContextを使うと、どこに何があるのか把握しづらくなることがあります。
まとめ
ここまで、useContext の基本から実務的なシナリオまでを順に見てきました。 Context API自体はReactに組み込まれており、フックとして活用するとコードがすっきりしやすいです。 一方で、データの更新範囲が広くなるとレンダリング回数が増えるなどの注意点もあります。 そこで、必要に応じてContextを分割するなど、使い方を工夫すると良いかもしれません。 初心者の皆さんは、まずは少人数のコンポーネントで使う練習をするだけでも、はっきりメリットを感じられるのではないでしょうか。