カテゴリ: React 更新日: 2026/03/22

Reactでネストしたコンポーネントにデータを渡す方法を完全解説!Context APIで深い階層も簡単に

ネストしたコンポーネントにデータを渡す方法
ネストしたコンポーネントにデータを渡す方法

先生と生徒の会話形式で理解しよう

生徒

「コンポーネントが何階層も深くなっているとき、一番下の子コンポーネントにデータを渡すにはどうすればいいんですか?propsをずっと渡し続けるしかないですか?」

先生

「propsを何段も渡し続けるのはProps drillingという問題で、コードが複雑になってしまいます。Context APIを使えば、ネストがどれだけ深くても直接データを届けられますよ。」

生徒

「どんな場面で使えばいいのか、実際のコードで見せてもらえますか?」

先生

「もちろんです。propsとContext APIの両方を比べながら、具体的に確認していきましょう!」

1. ネストしたコンポーネントとは?まず構造を理解しよう

1. ネストしたコンポーネントとは?まず構造を理解しよう
1. ネストしたコンポーネントとは?まず構造を理解しよう

Reactでアプリを作るとき、画面の各部品を「コンポーネント」という単位に分けて組み合わせます。コンポーネントの中にさらにコンポーネントを入れることをネストといいます。日本語では「入れ子」とも呼びます。

たとえばブログサイトであれば、ページ全体を管理するAppコンポーネントの中にHeaderコンポーネントがあり、その中にNavコンポーネント、さらにその中にNavItemコンポーネントがある、というように階層が深くなっていきます。

このようにネストが深くなった構造で、一番上の親コンポーネントが持つデータを一番深い子コンポーネントに渡したいとき、どうすればよいかが大きな課題になります。素直にpropsを使う方法と、Context APIを使う方法の2通りがあるので、それぞれの特徴を理解しておきましょう。

2. propsでネストしたコンポーネントにデータを渡す書き方

2. propsでネストしたコンポーネントにデータを渡す書き方
2. propsでネストしたコンポーネントにデータを渡す書き方

まず、propsを使って深い階層のコンポーネントにデータを渡す基本的な書き方を見てみましょう。propsとは、親コンポーネントから子コンポーネントへデータを渡すための仕組みです。


import React from "react";

function App() {
  const siteName = "React学習サイト";

  // HeaderにpropsとしてsiteNameを渡す
  return <Header siteName={siteName} />;
}

function Header({ siteName }) {
  // HeaderはsiteNameをNavに渡すだけ(自分では使わない)
  return <Nav siteName={siteName} />;
}

function Nav({ siteName }) {
  // NavもsiteNameをNavItemに渡すだけ
  return <NavItem siteName={siteName} />;
}

function NavItem({ siteName }) {
  // ここでようやくsiteNameを使う
  return <p>サイト名:{siteName}</p>;
}

export default App;
画面に「サイト名:React学習サイト」と表示されます。ただし、HeaderとNavはsiteNameをまったく使っておらず、ただ次のコンポーネントに渡すためだけにpropsを受け取っています。これがProps drillingと呼ばれる問題です。

2〜3階層程度であればpropsでも十分ですが、階層が5段、6段と深くなったり、渡すデータが増えたりすると、コードの読みやすさが急激に下がっていきます。このような状況を解決するのがContext APIです。

3. Context APIを使ってネストしたコンポーネントに直接データを届ける

3. Context APIを使ってネストしたコンポーネントに直接データを届ける
3. Context APIを使ってネストしたコンポーネントに直接データを届ける

Context APIを使えば、途中のコンポーネントを経由せずに、深い階層のコンポーネントへ直接データを届けることができます。先ほどの例をContext APIで書き直してみましょう。


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

// Contextを作成する
const SiteContext = createContext("");

function App() {
  const siteName = "React学習サイト";

  return (
    // Providerで囲んでvalueにデータをセット
    <SiteContext.Provider value={siteName}>
      <Header />
    </SiteContext.Provider>
  );
}

function Header() {
  // siteNameを受け取らなくていい
  return <Nav />;
}

function Nav() {
  // こちらも受け取らなくていい
  return <NavItem />;
}

function NavItem() {
  // useContextで直接データを取り出す
  const siteName = useContext(SiteContext);
  return <p>サイト名:{siteName}</p>;
}

export default App;
画面に「サイト名:React学習サイト」と表示されます。HeaderとNavにはpropsが一切なくなり、NavItemがuseContextを使ってContextから直接siteNameを取り出しています。コードがスッキリしました。

階層がどれだけ深くても、Providerの内側であればuseContextひとつでデータを取り出せます。途中のコンポーネントはデータのことを一切知らなくてよいので、それぞれのコンポーネントが自分の役割だけに集中できます。

4. オブジェクトを渡して複数データを深い階層に届ける方法

4. オブジェクトを渡して複数データを深い階層に届ける方法
4. オブジェクトを渡して複数データを深い階層に届ける方法

実際のアプリでは、ひとつのデータだけでなく複数のデータをまとめて深い階層に渡したい場面がよくあります。Providerのvalueにオブジェクトをセットすることで、複数のデータを一度にまとめて渡すことができます。

オブジェクトとは、{ キー名: 値 }の形で複数のデータをひとまとめにしたデータの型のことです。名前と年齢をまとめるなら{ name: "田中", age: 30 }のように書きます。


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

const ProfileContext = createContext(null);

function App() {
  const profileData = {
    name: "鈴木さん",
    job: "エンジニア",
    level: "中級",
  };

  return (
    <ProfileContext.Provider value={profileData}>
      <PageLayout />
    </ProfileContext.Provider>
  );
}

function PageLayout() {
  return (
    <div>
      <Sidebar />
      <MainArea />
    </div>
  );
}

function Sidebar() {
  // Sidebarは中間コンポーネントなので何も受け取らない
  return <UserCard />;
}

function UserCard() {
  // 必要なデータだけ取り出す(分割代入)
  const { name, job } = useContext(ProfileContext);
  return (
    <div class="card p-3">
      <p><i class="bi bi-person-fill"></i> {name}</p>
      <p><i class="bi bi-briefcase-fill"></i> {job}</p>
    </div>
  );
}

function MainArea() {
  const { level } = useContext(ProfileContext);
  return <p>スキルレベル:{level}</p>;
}

export default App;
画面にカード形式で「鈴木さん」「エンジニア」が表示され、その下に「スキルレベル:中級」と表示されます。UserCardとMainAreaはそれぞれ必要なデータだけをContextから取り出しており、Sidebarはpropsを何も受け取っていません。

const { name, job } = useContext(ProfileContext)のように書くと、オブジェクトから必要なプロパティだけを取り出せます。この書き方を分割代入と呼び、Reactのコードでは非常によく使われます。

5. useStateと組み合わせてネスト先からデータを更新する方法

5. useStateと組み合わせてネスト先からデータを更新する方法
5. useStateと組み合わせてネスト先からデータを更新する方法

Context APIは読み取るだけでなく、深い階層のコンポーネントからデータを変更することも可能です。useStateの状態と更新関数をまとめてContextで共有する方法が広く使われています。

useStateとは、変化する値をコンポーネント内で管理するためのReactフックです。フックとは、Reactが用意している特別な関数のことです。useStateを使うと、値が変わったときに画面が自動的に更新されます。


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

const NoticeContext = createContext(null);

function App() {
  const [notice, setNotice] = useState("お知らせはありません");

  return (
    // 値と更新関数をオブジェクトにまとめてvalueに渡す
    <NoticeContext.Provider value={{ notice, setNotice }}>
      <PageWrapper />
    </NoticeContext.Provider>
  );
}

function PageWrapper() {
  return (
    <div>
      <NoticeBoard />
      <AdminArea />
    </div>
  );
}

function NoticeBoard() {
  // noticeだけ取り出して表示する
  const { notice } = useContext(NoticeContext);
  return (
    <div class="alert alert-primary">
      <i class="bi bi-megaphone-fill"></i> {notice}
    </div>
  );
}

function AdminArea() {
  // setNoticeを取り出してお知らせを変更できるようにする
  const { setNotice } = useContext(NoticeContext);
  return (
    <div>
      <button class="btn btn-sm btn-outline-secondary" onClick={() => setNotice("本日はメンテナンスのためサービスを停止します")}>
        お知らせを更新する
      </button>
    </div>
  );
}

export default App;
画面に「お知らせはありません」と表示されます。「お知らせを更新する」ボタンを押すと「本日はメンテナンスのためサービスを停止します」に変わります。NoticeBoardとAdminAreaはpropsなしで状態を共有しています。

このように更新関数もContextに含めることで、アプリのどこに配置されたコンポーネントからでも状態を変更できます。状態の変更は即座にContextを使っているすべてのコンポーネントに反映されます。

6. Contextを別ファイルに切り出してネストの深いアプリを管理しやすくする

6. Contextを別ファイルに切り出してネストの深いアプリを管理しやすくする
6. Contextを別ファイルに切り出してネストの深いアプリを管理しやすくする

アプリが大きくなってくると、Contextの定義をAppコンポーネントの中に書き続けるのが難しくなってきます。そこで、ContextとProviderを専用の別ファイルに切り出す構成が一般的に使われています。

専用ファイルに切り出す利点は、コードの見通しがよくなることと、どこからでも同じContextをインポートして使えることです。またカスタムフックと組み合わせると、useContextを毎回書かなくてよくなります。カスタムフックとは、useContextなどのフックをラップして独自に作った関数のことで、コードの再利用性を高めます。

ファイル構成の一例としては、contexts/ThemeContext.jsxのようなフォルダにContextファイルをまとめておくやり方があります。このファイルの中でcreateContext、Providerコンポーネント、カスタムフックの3つをまとめてエクスポートしておきます。使う側のコンポーネントはインポートするだけでよいので、管理がとても楽になります。

ネストが深いアプリほど、このようなファイル分割の恩恵を感じやすいです。どのContextがどんなデータを管理しているかがファイル名で一目でわかるようになり、チーム開発でも迷わずコードを読み書きできるようになります。

7. propsとContext APIをどう使い分ければいいか

7. propsとContext APIをどう使い分ければいいか
7. propsとContext APIをどう使い分ければいいか

ここまでpropsとContext APIの両方を見てきました。どちらを使うか迷ったときの判断基準をまとめておきましょう。

まず、コンポーネントの階層が2〜3段程度で、渡すデータが少ない場合はpropsで十分です。propsはシンプルで直感的なため、コードが読みやすく、データの流れも追いやすいです。

一方で、次のような場合はContext APIが向いています。コンポーネントの階層が4段以上深い場合、アプリ全体で使うログイン情報やテーマ設定などを共有したい場合、途中のコンポーネントがデータをまったく使わないのにpropsを受け取り続けなければならない場合などが代表的な例です。

また、Context APIを使いすぎることにも注意が必要です。Contextの値が変わると、そのContextを参照しているすべてのコンポーネントが再描画されます。頻繁に値が変わるデータをContextに入れすぎると、パフォーマンスに影響が出ることがあります。「アプリ全体で共有する必要があるデータかどうか」を考えながら使うのが、長く使えるコードを書くコツです。

カテゴリの一覧へ
新着記事
New1
React
Reactでネストしたコンポーネントにデータを渡す方法を完全解説!Context APIで深い階層も簡単に
New2
React
useEffectの基本!副作用処理の意味と役割をやさしく解説
New3
React
Reactで複数のStateを管理する方法!オブジェクトや配列を使った実践的な使い方
New4
Next.js
Next.jsのTemplateでクライアント側状態保持を防ぐ方法を徹底解説!Layoutとの違いも初心者向けに完全理解
人気記事
No.1
Java&Spring記事人気No1
React
ReactとTypeScriptの環境構築をやさしく解説!Viteとtsconfigの設定も丁寧に紹介
No.2
Java&Spring記事人気No2
React
Reactとは?初心者でもわかるReact.jsの基本概念と特徴をやさしく解説
No.3
Java&Spring記事人気No3
Next.js
Next.jsのRoute Groupの使い方を完全ガイド!App Routerでフォルダ構成を整理する方法
No.4
Java&Spring記事人気No4
React
ReactのJSXとJavaScriptの違いを完全ガイド!初心者でもわかるReactのJSX入門
No.5
Java&Spring記事人気No5
React
Reactでキーボードイベントを活用する方法!onKeyDown, onKeyUp, onKeyPressを初心者向けに解説
No.6
Java&Spring記事人気No6
React
ReactのJSXはどうやって動く?Babelによるコンパイルの仕組みをやさしく解説
No.7
Java&Spring記事人気No7
React
Reactでフォーカスイベントを制御する方法!onFocusとonBlurを初心者向けに解説
No.8
Java&Spring記事人気No8
React
React Context APIとは?初心者向けに仕組みと役割をわかりやすく解説