レビュー済み·難易度: 中級·更新: 2026-04-15

React Context で軽量なグローバル状態を管理する

useState + createContext + useContext を組み合わせてグローバルな状態を管理する最小パターン。Zustand などの外部ライブラリが不要なケースに適している。

reactstate-management

対応バージョン

react 19typescript 5

前提環境

React hooks(useState, useContext)の基本を理解していること

概要

createContext + useContext を使うことで props drilling を避けつつ、 軽量なグローバル状態を実現できる。テーマ切り替えや認証状態など、更新頻度が低い値に適している。

実装

1. Context と Provider を定義する

// src/contexts/ThemeContext.tsx
import { createContext, useContext, useState } from "react";

type Theme = "light" | "dark";

type ThemeContextValue = {
  theme: Theme;
  toggleTheme: () => void;
};

const ThemeContext = createContext<ThemeContextValue | null>(null);

export function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [theme, setTheme] = useState<Theme>("light");

  const toggleTheme = () => {
    setTheme((prev) => (prev === "light" ? "dark" : "light"));
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

/** Context の値を取得するカスタムフック */
export function useTheme(): ThemeContextValue {
  const ctx = useContext(ThemeContext);
  if (!ctx) {
    throw new Error("useTheme は ThemeProvider の内側で使用してください");
  }
  return ctx;
}

2. Provider でアプリをラップする

// src/app/layout.tsx
import { ThemeProvider } from "@/contexts/ThemeContext";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="ja">
      <body>
        <ThemeProvider>{children}</ThemeProvider>
      </body>
    </html>
  );
}

3. 任意の子コンポーネントで使う

// src/components/ThemeToggle.tsx
"use client";

import { useTheme } from "@/contexts/ThemeContext";

export function ThemeToggle() {
  const { theme, toggleTheme } = useTheme();

  return (
    <button
      onClick={toggleTheme}
      className="rounded border px-3 py-1 text-sm"
    >
      現在: {theme === "light" ? "ライト" : "ダーク"} → 切り替え
    </button>
  );
}

ポイント

  • Context の初期値を null にして、Provider 外での使用をエラーで検出する
  • カスタムフック(useTheme)でアクセスを統一する
  • Next.js App Router で使う場合、Provider コンポーネントは "use client" が必要
  • 頻繁に変わる状態(例: 入力中の値)には適さない。その場合は Zustand を使う

注意点

Context は頻繁に変わる状態には向かない。更新頻度が高い場合は Zustand などを検討する

関連サンプル