【React修行日記】Context API + TypeScript 型の渡し方
学習の目的
- Contextについて理解する
- Context APIの使い方について理解する
- Context APIとTypeScriptを使用してテーマ切替の実装ができるようになる
Contextとは
Reactでは通常、コンポーネント間でデータを受け渡すときにpropsを使い、親コンポーネントから子コンポーネントへ情報が伝達される。
しかし、階層が深くなると 「props drilling」と呼ばれる状態が発生しやすくなる。(情報のバケツリレー)
そこで、Contextを使用することによってあらかじめ上位コンポーネントで用意された値を、任意の子孫コンポーネントで参照できるようになる。
Contextを使用する前に検討すること(公式より)
Contextは便利だが、安易に使いすぎるとアプリの設計やコンポーネントの再利用性に影響する可能性がある。
公式でも、Contextを導入する前には検討すべきことが挙げられている。
-
まずはpropsで渡す
- 数レベルの階層であれば、普通にpropsを渡す方がコードがシンプルで理解しやすい
- どのコンポーネントがどのデータを使うかが明確になるので、メンテナンス性が高くなる
-
childrenとして渡す
- 中間コンポーネントがただデータを下に流すだけになっている場合は、childrenを使ってコンポーネントを抽出すると良い
- 例:
<Layout><Posts posts={posts} /></Layout>のようにデータを必要なコンポーネントに直接渡す
上記の方法では解決できない場合や、グローバルに共有したい値がある場合にContextを使うのが適切。
Context APIとは
Context API は「コンポーネント間でデータを共有するためのReact公式の仕組み」。
基本的な流れは以下。
1. Contextの作成
まずは共有したい値の型を定義して、createContextでコンテキストを作る。
import { createContext } from "react";
const MyContext = createContext<string | null>(null);
- MyContext は作成したContextオブジェクト
-
<MyContext>を使うことで値を提供したり、子コンポーネントで利用できる - 型を付けておくことで、TypeScriptでの型補完や安全性が確保される
2. Provider役のコンポーネントを用意
次に、Contextに渡す値をまとめて管理するラッパーコンポーネントを作る。
export function MyProvider({ children }: { children: React.ReactNode }) {
const value = "Hello, Context!";
return <MyContext value={value}>{children}</MyContext>;
}
- MyProvider はContextに値を渡す役割を持つ
-
childrenをラップすることで、ツリー内のどの子孫コンポーネントでも値を利用可能 - 状態や関数(useState / useReducer)をまとめて渡すこともできる
React18以前では<MyContext.Provider value={...}>のように書く必要があったが、React19以降は<MyContext value={...}>とContextオブジェクト自体をJSXコンポーネントとして使えるようになった。
なお、<MyContext.Provider>の記法も依然として動作するが、今後は新しい書き方が推奨されている。
3. 子コンポーネントで利用
最後に、子コンポーネントでContextの値を参照する。
import { use } from "react";
function Child() {
const ctx = use(MyContext);
if (!ctx) throw new Error("MyContextが使える範囲ではありません");
return <div>{ctx}</div>;
}
-
use(MyContext)でProviderから渡された値を取り出す - nullチェックを入れることで、Provider外での誤使用を防止できる
- 取得した値は通常の変数として扱えるため、propsを経由せずに値にアクセス可能
React19以降ではuse(MyContext)で値を取り出せる。従来通りuseContext(MyContext)を使うことも可能だが、useはifやforといったブロック内でも呼び出せる点が特徴。
Context APIとTypeScriptを使用したテーマ切替
今回はContext APIを使用して簡易的なテーマ切替を実装してみた...!

※今回はテーマの切り替えが視覚的にわかりやすくなるよう、簡単なコンテンツを追加
Contextの作成
// ThemeContext.tsx
import { createContext } from "react";
export type Theme = "light" | "dark";
export type ThemeContextType = {
theme: Theme;
toggleTheme: () => void;
};
export const ThemeContext = createContext<ThemeContextType | null>(null);
-
Theme型でテーマの種類を "light" / "dark" に限定 -
ThemeContextTypeでContextが持つ値の形(themeとtoggleTheme)を定義 -
createContext<ThemeContextType | null>(null)
→ 初期値をnullにしておくことで、Providerの外でContextを使った場合にnullが返り、明示的にエラーを投げて検出できる
Providerコンポーネントの作成
// ThemeProvider.tsx
import { useState } from "react";
import { ThemeContext, type Theme } from "./ThemeContext";
export const ThemeProvider = ({ children }: { children: React.ReactNode }) => {
const [theme, setTheme] = useState<Theme>("light");
const toggleTheme = () => {
setTheme((prev) => (prev === "light" ? "dark" : "light"));
};
return <ThemeContext value={{ theme, toggleTheme }}>{children}</ThemeContext>;
};
-
ThemeContext.Providerの代わりにThemeContextを直接JSXで使う - 状態管理(useState)はProvider内部で完結しているため、外部からも簡単に利用できる
テーマを使うコンポーネント
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { ThemeContext } from "@/context/ThemeContext";
import { use } from "react";
export default function ThemeToggleDemo() {
const ctx = use(ThemeContext);
if (!ctx) throw new Error("Error");
const { theme, toggleTheme } = ctx;
return (
<div
className={`flex flex-col items-center gap-6 transition-colors p-8 ${
theme === "light" ? "bg-white text-black" : "bg-gray-900 text-white"
}`}
>
<h1 className="text-3xl font-bold">テーマ切替デモ</h1>
{/* ボタン */}
<Button
onClick={toggleTheme}
variant={theme === "light" ? "default" : "secondary"}
>
現在のテーマ: {theme} (クリックで切り替え)
</Button>
{/* サンプルコンテンツ */}
<div className="flex flex-col md:flex-row gap-4">
<Card
className={`transition-colors ${
theme === "light" ? "bg-white" : "bg-gray-800 text-white"
}`}
>
<CardContent>Card: テーマ連動</CardContent>
</Card>
</div>
</div>
);
}
※shadcn/uiを使用
-
use(ThemeContext)を使って値を取得 - Providerの設定漏れを検出するためのエラーチェック
→初期値をnullにしているため、Providerの外で利用された場合にエラーを投げて検知できるようにしている
今回は学習用のため、各コンポーネント内でテーマに応じてクラスを直接切り替えている。
実際のアプリ開発では、data-theme属性やCSS変数、Tailwindのdark:モードなどを併用すると、スタイル管理をより簡潔に保てる。
アプリ全体にProviderを適用
// App.tsx
import ThemeProvider from "@/context/ThemeProvider";
import ThemeToggleDemo from "@/components/ThemeToggleDemo";
function App() {
return (
<ThemeProvider>
<ThemeToggleDemo />
</ThemeProvider>
);
}
export default App;
- ルートでProviderをラップすることで、下層のコンポーネントすべてでテーマ情報が利用可能になる
まとめ
- Contextはpropsのバケツリレーを解消し、コンポーネント間のデータ共有をシンプルにできる
- ただし、使いすぎると再利用性を下げるため、まずはpropsやchildrenの利用を検討するのが望ましい
- TypeScriptと組み合わせることで、型安全かつ保守性の高いグローバル状態管理が可能になる
参考
Discussion