React 速習チュートリアル

React useContext Hook

1. React Contextとは?

React Contextは、ステート(State)をグローバルに管理するための仕組みです。

useState フック単体で管理する場合よりも、深くネスト(階層化)されたコンポーネント間でステートをはるかに簡単に共有することができます。

1.1 課題:プロップ・ドリリング(Prop Drilling)

通常、ステートはアクセスを必要とするコンポーネント群の中で、最も上位にある親コンポーネントが保持すべきです。

しかし、多くのコンポーネントがネストされている場合、最上位と最下位のコンポーネントで同じステートを使いたいとき、Contextなしでは各コンポーネントに「プロップス(Props)」としてステートをバケツリレーのように渡していく必要があります。これを 「プロップ・ドリリング(Prop Drilling)」 と呼びます。

実装例:ネストされたコンポーネントへのプロップス渡し

import { useState } from 'react';
import { createRoot } from 'react-dom/client';

function Component1() {
  const [user, setUser] = useState("ライナス");

  return (
    <>
      <h1>{`こんにちは、${user}さん!`}</h1>
      {/* userをComponent2に渡す */}
      <Component2 user={user} />
    </>
  );
}

function Component2({ user }) {
  return (
    <>
      <h1>コンポーネント 2</h1>
      {/* Component2自体はuserを必要としないが、Component3のために渡す必要がある */}
      <Component3 user={user} />
    </>
  );
}

function Component3({ user }) {
  return (
    <>
      <h1>コンポーネント 3</h1>
      <h2>{`また会いましたね、${user}さん!`}</h2>
    </>
  );
}

createRoot(document.getElementById('root')).render(
  <Component1 />
);

この例では、Component2 はステートを必要としていないにもかかわらず、Component3 に届けるためだけにステートを中継しなければなりません。これが開発における「無駄な仲介」となります。

2. React Contextによる解決策

この問題を解決するために、Context(コンテキスト)を作成します。

2.1 Contextの作成

Contextを作成するには、createContext をインポートして初期化します。

import { useState, createContext, useContext } from 'react';
import { createRoot } from 'react-dom/client';

// Contextの初期化
const UserContext = createContext();

次に、ステートを必要とするコンポーネントツリーを Context Provider(コンテキスト・プロバイダー) でラップします。

2.2 Context Provider

子コンポーネントをプロバイダーでラップし、共有したい値を value 属性に指定します。

function Component1() {
  const [user, setUser] = useState("ライナス");

  return (
    <UserContext.Provider value={user}>
      <h1>{`こんにちは、${user}さん!`}</h1>
      <Component2 />
    </UserContext.Provider>
  );
}

これで、このツリーに含まれるすべてのコンポーネントが user コンテキストに直接アクセスできるようになります。

2.3 useContextフックの利用

子コンポーネントでコンテキストを使用するには、useContext フックを介してアクセスします。まず、インポートに useContext を含めてください。

import { useState, createContext, useContext } from "react";

これで、すべてのコンポーネントで user コンテキストを呼び出すことができます。

function Component3() {
  // useContextでContextの値を直接取得
  const user = useContext(UserContext);

  return (
    <>
      <h1>コンポーネント 3</h1>
      <h2>{`また会いましたね、${user}さん!`}</h2>
    </>
  );
}

3. 実装の完全なコード例

React Contextを使用した最終的なコード構成は以下のようになります。

import { useState, createContext, useContext } from 'react';
import { createRoot } from 'react-dom/client';

// 1. Contextを作成
const UserContext = createContext();

function Component1() {
  const [user, setUser] = useState("ライナス");

  return (
    // 2. Providerでラップして値を共有
    <UserContext.Provider value={user}>
      <h1>{`こんにちは、${user}さん!`}</h1>
      <Component2 />
    </UserContext.Provider>
  );
}

function Component2() {
  return (
    <>
      <h1>コンポーネント 2</h1>
      {/* Component2は何もプロップスを受け取らず、渡さない */}
      <Component3 />
    </>
  );
}

function Component3() {
  // 3. useContextを使用して必要なコンポーネントでデータを受け取る
  const user = useContext(UserContext);

  return (
    <>
      <h1>コンポーネント 3</h1>
      <h2>{`また会いましたね、${user}さん!`}</h2>
    </>
  );
}

createRoot(document.getElementById('root')).render(
  <Component1 />
);