【ITニュース解説】UseContext in React JS
2025年10月03日に「Dev.to」が公開したITニュース「UseContext in React JS」について初心者にもわかりやすく解説しています。
ITニュース概要
Reactの`useContext`は、コンポーネント間でデータを共有する機能だ。親から子へ手動で何度もデータを渡す「prop drilling」を避け、テーマ設定やログインユーザー情報など、多くのコンポーネントで共通して使う情報を効率的に管理できる。
ITニュース解説
Reactアプリケーションを開発する際、複数のコンポーネント間でデータを共有する機会は頻繁にある。通常、親コンポーネントから子コンポーネントへデータを渡すには、「プロップス」(propertiesの略)という仕組みを使う。しかし、アプリケーションの規模が大きくなり、コンポーネントの階層が深くなると、このプロップスを渡す方法では問題が生じることがある。
例えば、あるデータをアプリケーションの最上位のコンポーネントが持っていて、それが何層も下の子コンポーネントで必要になる場合を考える。このとき、データを受け渡す途中のコンポーネントは、そのデータ自体を使う必要がないにもかかわらず、単に次の子コンポーネントに渡すためだけにプロップスを受け取り、さらに渡すという作業を繰り返さなければならない。この現象を「プロップドリリング」と呼ぶ。プロップドリリングは、コードの可読性を著しく低下させ、メンテナンスを困難にする。なぜなら、データがどこから来てどこへ行くのかを追跡するのが難しくなり、途中のコンポーネントが本来の目的とは関係ないプロップスを受け取ることで、そのコンポーネントの役割が曖昧になるからである。
このプロップドリリングの問題を効率的に解決するためにReactが提供しているのが、「Context」という仕組みと、それを利用するための「useContext」フックである。useContextフックは、関数コンポーネント内でReact Contextから直接値を取り出すための方法を提供する。これにより、プロップスを手動で何層も渡していく必要がなくなり、コンポーネントツリー内のどこからでも特定のデータにアクセスできるようになる。
Contextとは、アプリケーション全体や特定のコンポーネントツリー内で、複数のコンポーネントが共通してアクセスしたいデータを格納し、提供するための仕組みである。これは、特定のデータセットをアプリケーションのさまざまな場所に「グローバル変数」のようにアクセスできるようにする、と考えるとわかりやすいかもしれない。ただし、実際のグローバル変数とは異なり、ReactのContextは特定の範囲内でデータを共有し、その変更に応じて関連するコンポーネントだけを再レンダリングする、というReactのコンポーネント指向の恩恵を受けることができる。
useContextフックを使うべきなのは、特定のデータがアプリケーション内の多くのコンポーネントによって頻繁に参照される必要がある場合である。例えば、アプリケーションのテーマ設定(ダークモードかライトモードか)、現在ログインしているユーザーの情報、多言語対応の現在の言語設定などが挙げられる。これらの情報は、アプリケーション内の非常に多くのコンポーネントに影響を与えるため、プロップドリリングで渡すのは非効率的かつ複雑になる。
useContextを実際に使うための基本的な流れを見てみよう。まず、共有したいデータを保持するための「箱」を作るイメージで、createContext()関数を使ってContextオブジェクトを生成する。例えば、ユーザー情報を共有するためのContextを作る場合は、const UserContext = createContext(); のように記述する。このUserContextが、データを供給する側と消費する側をつなぐ役割を果たす。
次に、このContextオブジェクトには、データを「供給」するための特別なコンポーネントである「Provider」(供給者)が関連付けられる。このProviderコンポーネントは、UserContext.Providerのように記述され、valueという特別なプロップを通じて、共有したいデータを下位のコンポーネントに提供する。Providerで囲まれた範囲(コード例では{props.children}で示される部分)にあるすべてのコンポーネントは、このvalueプロップで提供されたデータにアクセスできるようになる。
Providerが提供するデータは、Reactの「状態」(state)として管理されることが多い。コード例では、useStateフックを使って、usernameというユーザー名を管理している。const [username, setUsername] = useState(''); のように書くことで、usernameというデータとそのデータを更新するための関数setUsernameをセットで用意している。このusernameとその更新関数をContextのvalueとして提供することで、下位コンポーネントはユーザー名を参照するだけでなく、必要に応じて更新することも可能になる。
ここで重要なのが、useMemoフックの使用である。Providerに渡すvalueプロップがオブジェクトの場合、Reactはコンポーネントが再レンダリングされるたびに、たとえ中身が変わっていなくても新しいオブジェクトを生成してしまう可能性がある。この新しいオブジェクトの生成は、valueを受け取っている下位コンポーネントにも再レンダリングを促してしまうことがあるため、アプリケーションのパフォーマンスを低下させる原因となる。useMemoフックを使うと、指定した依存関係(コード例では[username])が変更されない限り、以前計算された値を使い回すことができる。これにより、valueプロップのオブジェクトが不要に再生成されることを防ぎ、無駄な再レンダリングを抑制してアプリケーションのパフォーマンスを向上させる効果がある。
実際にContextから値を利用する側、つまりデータを「消費」する側では、関数コンポーネント内でuseContext(UserContext)のように記述するだけで、Providerから提供されたvalueに直接アクセスできる。これにより、コンポーネントツリーのどの階層にいても、手軽に共通データを利用したり更新したりすることが可能となる。
このように、useContextフックはReactアプリケーションにおけるデータ共有の方法をシンプルにし、長年の課題であったプロップドリリングの問題を解消する強力なツールである。ContextとProvider、そしてuseContextフックを適切に組み合わせることで、アプリケーションの構造をより効率的で管理しやすいものにでき、特に多くのコンポーネントが共通してアクセスする必要があるデータには、この仕組みの活用が強く推奨される。