1
0

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の基本

Posted at

はじめに

Reactではコンポーネント間で値を共有する際、propsを使うのが基本です。
しかし、ツリーの深い場所まで同じデータを渡す場合は、バケツリレー が発生しがちです。

このような煩雑さを解決してくれるのが、ReactのContext APIです。

本記事では、Context の概要や用途、サンプルコードなどを記載します。

Contextとは?

“Context lets a component receive information from a parent component without explicitly passing it through every level of the tree.”
Passing Data Deeply with Context

ReactのContext APIは、ツリー構造の深い位置にあるコンポーネントでも、親から明示的にpropsを渡さずに値を受け取れる仕組みです。

例えば、次のようにProviderで囲っておけば、どの深さの子コンポーネントからでもその値にアクセスできます。

.tsx
<MyContext.Provider value={...}>
  <App />
</MyContext.Provider>

Contextが向いている場面

Context の実際の用途としては、以下のような「アプリ全体(異なる複数階層のコンポーネント)で使われる、めったに変わらない値」に適しています。

  • テーマ設定(ライト/ダーク)
  • 現在の言語(i18n)
  • ログインユーザー情報

逆に「フォームの入力内容」や「一時的なUI状態」など、頻繁に変わるデータには向いていません。Contextで扱うと、パフォーマンスの低下やコードの複雑化を招く可能性があります。

実装サンプル

本記事では、以下のような string型の配列を2つContextで管理する例を紹介します。

名前 内容
fruitChoices 果物の選択肢(例:りんご、バナナ)
vegetableChoices 野菜の選択肢(例:にんじん、ピーマン)

あくまで Context の実装方法をわかりやすく伝えるためのサンプルです。中身はただの定数なので、実用途としては、わざわざ Context 化する必要はないです。

開発環境

開発環境は以下の通りです。

  • Windows11
  • React 18.3.1
  • React Router 7.5.2
  • TypeScript 5.8.3
  • Vite 6.3.4
  • Node.js 22.13.1
  • npm 11.0.0

1. Contextの定義とProviderの作成

ChoiceContext.tsx
import { createContext, ReactNode, useContext } from "react";

type ChoiceContextType = {
  fruitChoices: string[];
  vegetableChoices: string[];
};

const ChoiceContext = createContext<ChoiceContextType>({
  fruitChoices: [],
  vegetableChoices: [],
});

export const ChoiceProvider = ({ children }: { children: ReactNode }) => {
  const fruitChoices = ["りんご", "みかん", "バナナ"];
  const vegetableChoices = ["にんじん", "ほうれん草", "ピーマン"];

  return (
    <ChoiceContext.Provider value={{ fruitChoices, vegetableChoices }}>
      {children}
    </ChoiceContext.Provider>
  );
};

/** Contextの値を取得するカスタムフック */
export const useChoices = () => useContext(ChoiceContext);
  • 初期値をcreateContextにそのまま渡すことで、型をシンプルにしています(undefinedチェックも不要)

2. Provider で Context で保持した状態を利用するコンポーネント全体をラップする

App.tsx
import { Route, Routes } from "react-router-dom";
import ChoiceViewer from "./pages/ChoiceViewer";
import { ChoiceProvider } from "./contexts/ChoiceContext";
import { NotFount } from "./pages/NotFound";

function App() {
  return (
    <Routes>
      <Route
        path="/choice-viewer"
        element={
          <ChoiceProvider>
            <ChoiceViewer />
          </ChoiceProvider>
        }
      />
      <Route path="*" element={<NotFount />} />
    </Routes>
  );
}

export default App;

3. Contextを使って値を表示する

ChoiceViewer.tsx
import { useChoices } from "./ChoiceContext";

const ChoiceViewer = () => {
  const { fruitChoices, vegetableChoices } = useChoices();

  return (
    <div>
      <h3>果物の選択肢</h3>
      <ul>
        {fruitChoices.map((fruit) => (
          <li key={fruit}>{fruit}</li>
        ))}
      </ul>

      <h3>野菜の選択肢</h3>
      <ul>
        {vegetableChoices.map((vegetable) => (
          <li key={vegetable}>{vegetable}</li>
        ))}
      </ul>
    </div>
  );
};

export default ChoiceViewer;

image.png

「2. Provider で Context で保持した状態を利用するコンポーネント全体をラップする」をしないと、Context で保持した状態は取得できません。

image.png

まとめ

この記事では、ReactのContext APIについて解説しました。Contextを適切に活用することで、コードの可読性と保守性を高め、「バケツリレー」問題から解放された効率的なコンポーネント設計が可能になります。

参考

1
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?