1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【React】Context APIで理解する状態共有の仕組み

Last updated at Posted at 2025-12-10

はじめに

Reactでコンポーネントツリーをまたいでデータを共有したいとき、どのように実装していますか?

この記事では、ReactのContext APIについて、基本概念から実践的な使い方まで解説します。Props drillingの問題を解決し、より保守性の高いコードを書けるようになりましょう。

この記事で学べること

  • Props drillingの問題点
  • Context APIの基本構成と仕組み
  • 実践的な実装例
  • Context APIの適切な使い分け

Props drillingの問題点

まず、Context APIが解決する問題について見ていきます。

従来のprops渡しの課題

Reactでは、親コンポーネントから子コンポーネントへpropsでデータを渡します。しかし、深い階層のコンポーネントにデータを渡す場合、中間のコンポーネントを経由する必要があります。

この問題をProps drillingと呼びます。

具体例:3階層のコンポーネント

次のようなコンポーネント構造があるとします:

// 最上位のページコンポーネント
function UserProfilePage() {
  const user = { name: "田中太郎", role: "管理者" };
  
  return <ProfileContainer user={user} />;
}

// 中間コンポーネント(userを使わないが受け渡しが必要)
function ProfileContainer({ user }) {
  return (
    <div className="container">
      <UserInfo user={user} />
    </div>
  );
}

// 実際にuserを使うコンポーネント
function UserInfo({ user }) {
  return (
    <div>
      <p>名前: {user.name}</p>
      <p>役割: {user.role}</p>
    </div>
  );
}

この例では、ProfileContaineruserデータを使いませんが、UserInfoに渡すためだけに受け取っています。

Props drillingの流れ

黄色のProfileContainerは、データを中継するだけで実際には使っていません。これがProps drillingの問題点です。

Props drillingによる弊害

  • 中間コンポーネントの肥大化: 使わないpropsを受け渡すだけのコードが増える
  • 保守性の低下: データ構造の変更時に複数のコンポーネントを修正する必要がある
  • 再利用性の低下: 中間コンポーネントが特定のpropsに依存してしまう
  • 可読性の低下: データの流れが複雑になり、追跡が困難になる

Context APIの基本構成

Context APIは、この問題を解決するための仕組みです。コンポーネントツリー全体でデータを共有できるようになります。

3つの構成要素

Context APIは3つの要素で構成されています:

1. Context オブジェクト

createContext()で作成される、データの「入れ物」です。

import { createContext } from 'react';

const UserContext = createContext(null);

デフォルト値を設定できますが、通常はProviderで実際の値を提供します。

2. Provider(提供者)

Contextの値を設定し、配下のコンポーネントツリーに提供します。

<UserContext.Provider value={user}>
  {/* ここに配置されたコンポーネントはuserにアクセス可能 */}
</UserContext.Provider>

valueプロパティで実際のデータを渡します。

3. Consumer(消費者)

Contextの値を受け取る側です。関数コンポーネントではuseContext()フックを使います。

import { useContext } from 'react';

function UserInfo() {
  const user = useContext(UserContext);
  return <p>{user.name}</p>;
}

Context APIのデータの流れ

Providerでラップされたコンポーネントツリー内であれば、どの階層からでもuseContext()でデータにアクセスできます。

Context APIを使った場合の構造

ProfileContainerは何もpropsを受け取る必要がなくなり、UserInfoが直接Contextからデータを取得します。

実装例:ユーザー情報の共有

実際にContext APIを使った実装を見ていきましょう。

ステップ1: Contextの作成

まず、Contextオブジェクトを作成します。

// contexts/UserContext.js
import { createContext } from 'react';

export const UserContext = createContext(null);

別ファイルにすることで、複数のコンポーネントからimportして使えるようにします。

ステップ2: Providerの設定

アプリケーションの上位コンポーネントでProviderを設置します。

// App.js
import { UserContext } from './contexts/UserContext';

function App() {
  const user = {
    name: "田中太郎",
    role: "管理者",
    email: "tanaka@example.com"
  };

  return (
    <UserContext.Provider value={user}>
      <UserProfilePage />
    </UserContext.Provider>
  );
}

Providervalueに渡したuserオブジェクトが、配下の全コンポーネントで利用可能になります。

ステップ3: useContextでのデータ取得

必要なコンポーネントでuseContext()を使ってデータを取得します。

// components/UserInfo.js
import { useContext } from 'react';
import { UserContext } from '../contexts/UserContext';

function UserInfo() {
  const user = useContext(UserContext);
  
  if (!user) {
    return <p>ユーザー情報がありません</p>;
  }
  
  return (
    <div>
      <h2>ユーザー情報</h2>
      <p>名前: {user.name}</p>
      <p>役割: {user.role}</p>
      <p>メール: {user.email}</p>
    </div>
  );
}

中間コンポーネントを経由せず、直接userデータにアクセスできています。

ステップ4: カスタムフックでのラップ

useContext()を直接使うのではなく、カスタムフックでラップするのがベストプラクティスです。

// contexts/UserContext.js
import { createContext, useContext } from 'react';

const UserContext = createContext(null);

// カスタムフック
export function useUser() {
  const context = useContext(UserContext);
  
  if (context === null) {
    throw new Error('useUser must be used within a UserProvider');
  }
  
  return context;
}

export const UserContext = UserContext;

コンポーネントでの使用がシンプルになります:

import { useUser } from '../contexts/UserContext';

function UserInfo() {
  const user = useUser(); // useContext(UserContext)の代わり
  
  return (
    <div>
      <p>名前: {user.name}</p>
    </div>
  );
}

カスタムフックのメリット

  • エラーハンドリング: Provider外での使用を検出できる
  • 実装の隠蔽: Contextの実装詳細を隠せる
  • 型安全性の向上: TypeScriptで型推論が効きやすくなる
  • 将来的な変更が容易: 内部実装を変えてもコンポーネント側は変更不要

まとめ

Context APIは、Reactでコンポーネント間のデータ共有を実現する強力な仕組みです。

Context APIのメリット

  • Props drillingを解消できる
  • コードの保守性が向上する
  • コンポーネントの再利用性が高まる
  • データの流れが明確になる

Context APIのデメリット

  • 不適切に使うとパフォーマンスが低下する
  • データの流れが暗黙的になり、デバッグが難しくなる場合がある
  • テストが複雑になる

ベストプラクティス

  1. カスタムフックでラップする: エラーハンドリングと型安全性を向上
  2. Contextを適切に分割する: 関連性の低いデータは別々のContextに
  3. useMemoで最適化する: 不要な再レンダリングを防ぐ
  4. 適材適所で使う: すべてをContextで管理しない
1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?