Knowledge Center
技術情報などを公開しています
en ja zh
React入門 全12回シリーズ

コンテキストAPI:グローバルな状態管理の基礎(第10回)

By Author
image
Reactアプリケーションが複雑化すると、複数のコンポーネント間でデータを共有する必要性が増します。Propsを介してデータを渡す方法は有効ですが、深いコンポーネント階層では煩雑になることがあります。コンテキストAPIは、この問題を解決し、グローバルな状態管理を簡潔に実現する仕組みです。本連載の第10回では、コンテキストAPIの基本を学び、コンポーネント間でデータを共有する方法をハンズオン形式で習得します。第1回で構築したReact開発環境、第2回のJSX、第3回のProps、第4回の`useState`、第5回のイベントハンドリング、第6回のリストレンダリング、第7回の`useEffect`、第8回のフォーム、第9回のReact Routerの知識を基に、macOS環境で開発を進めます。本記事は、プログラミングの基礎知識を持つ読者を対象としています。

コンテキストAPIの基本概念

コンテキストAPIは、Reactが提供する機能で、コンポーネントツリー全体にデータを共有するための方法です。コンテキストを作成し、プロバイダー(Provider)でデータを供給し、コンシューマー(Consumer)またはuseContextフックでデータにアクセスします。これにより、Propsを何層も介してデータを渡す「Props Drilling」を回避できます。基本的な構造は以下の通りです。

import React, { createContext, useContext } from 'react';    
    
const MyContext = createContext();    
    
function App() {    
  return (    
    <MyContext.Provider value="共有データ">    
      <ChildComponent />    
    </MyContext.Provider>    
  );    
}    
    
function ChildComponent() {    
  const value = useContext(MyContext);    
  return <p>{value}</p>;    
}    

このコードでは、createContextでコンテキストを作成し、Providerで値を供給し、useContextで値を取得しています。

ハンズオン:テーマ切り替え機能の実装

コンテキストAPIの基本を理解するため、アプリケーション全体のテーマ(例:ライトモードとダークモード)を管理する機能を実装します。第1回で作成したプロジェクト(my-react-app)を使用します。まず、src/ThemeContext.jsを作成してコンテキストを定義します。

import React, { createContext, useContext, useState } from 'react';    
    
const ThemeContext = createContext();    
    
export function ThemeProvider({ children }) {    
  const [theme, setTheme] = useState('light');    
    
  const toggleTheme = () => {    
    setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));    
  };    
    
  return (    
    <ThemeContext.Provider value={{ theme, toggleTheme }}>    
      {children}    
    </ThemeContext.Provider>    
  );    
}    
    
export function useTheme() {    
  return useContext(ThemeContext);    
}    

このコードでは、ThemeContextを作成し、ThemeProviderコンポーネントでテーマ状態と切り替え関数を提供します。useThemeフックでコンテキスト値に簡単にアクセスできます。次に、src/App.jsを以下のように編集してテーマを適用します。

import React from 'react';    
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';    
import { ThemeProvider, useTheme } from './ThemeContext';    
import Home from './Home';    
import About from './About';    
import './App.css';    
    
function ThemeToggle() {    
  const { theme, toggleTheme } = useTheme();    
  return (    
    <button onClick={toggleTheme}>    
      {theme === 'light' ? 'ダークモード' : 'ライトモード'}に切り替え    
    </button>    
  );    
}    
    
function App() {    
  const { theme } = useTheme();    
    
  return (    
    <div className={`app-container ${theme}`}>    
      <h1>テーマ切り替えアプリケーション</h1>    
      <nav>    
        <Link to="/" style={{ margin: '0 10px' }}>    
          ホーム    
        </Link>    
        <Link to="/about" style={{ margin: '0 10px' }}>    
          アバウト    
        </Link>    
      </nav>    
      <ThemeToggle />    
      <Routes>    
        <Route path="/" element={<Home />} />    
        <Route path="/about" element={<About />} />    
      </Routes>    
    </div>    
  );    
}    
    
export default function Root() {    
  return (    
    <BrowserRouter>    
      <ThemeProvider>    
        <App />    
      </ThemeProvider>    
    </BrowserRouter>    
  );    
}    

このコードでは、ThemeProviderでアプリケーション全体を囲み、useThemeフックでテーマにアクセスします。ThemeToggleコンポーネントでテーマを切り替えます。Home.jsAbout.jsもテーマを反映するように編集します。src/Home.jsを以下のように変更します(About.jsも同様)。

import React from 'react';    
import { useTheme } from './ThemeContext';    
    
function Home() {    
  const { theme } = useTheme();    
  return (    
    <div className={`content ${theme}`}>    
      <h2>ホーム</h2>    
      <p>テーマ切り替えをお試しください!</p>    
    </div>    
  );    
}    
    
export default Home;    

src/About.jsも同様に編集します。

import React from 'react';    
import { useTheme } from './ThemeContext';    
    
function About() {    
  const { theme } = useTheme();    
  return (    
    <div className={`content ${theme}`}>    
      <h2>アバウト</h2>    
      <p>コンテキストAPIでテーマを管理しています。</p>    
    </div>    
  );    
}    
    
export default About;    

src/App.cssにテーマごとのスタイルを追加します。

.app-container {    
  text-align: center;    
  padding: 20px;    
  font-family: Arial, sans-serif;    
  min-height: 100vh;    
}    
    
.app-container.light {    
  background-color: #ffffff;    
  color: #000000;    
}    
    
.app-container.dark {    
  background-color: #333333;    
  color: #ffffff;    
}    
    
.content.light {    
  background-color: #f0f0f0;    
  padding: 20px;    
  border-radius: 5px;    
}    
    
.content.dark {    
  background-color: #555555;    
  padding: 20px;    
  border-radius: 5px;    
}    
    
nav {    
  margin: 20px 0;    
}    
    
nav a {    
  text-decoration: none;    
  font-size: 18px;    
}    
    
nav a.light {    
  color: #007bff;    
}    
    
nav a.dark {    
  color: #66b0ff;    
}    
    
nav a:hover {    
  text-decoration: underline;    
}    
    
button {    
  margin: 10px;    
  padding: 10px 20px;    
  font-size: 16px;    
  cursor: pointer;    
}    

開発サーバー(npm start)が起動している状態で保存すると、ブラウザにナビゲーションとテーマ切り替えボタンが表示されます。「ダークモードに切り替え」をクリックすると、背景色とテキスト色が変化し、ライトモードとダークモードが切り替わります。ホームとアバウトページでもテーマが適用されていることを確認できます。

ハンズオン:コンテキストの拡張

コンテキストAPIの柔軟性を体感するため、読者自身で機能を追加します。以下の課題を試してください。ThemeContext.jsを編集し、テーマに「セピアモード」を追加して、3つのテーマを切り替えられるようにします。以下は一例です。

import React, { createContext, useContext, useState } from 'react';    
    
const ThemeContext = createContext();    
    
export function ThemeProvider({ children }) {    
  const [theme, setTheme] = useState('light');    
    
  const toggleTheme = () => {    
    setTheme((prev) => {    
      if (prev === 'light') return 'dark';    
      if (prev === 'dark') return 'sepia';    
      return 'light';    
    });    
  };    
    
  return (    
    <ThemeContext.Provider value={{ theme, toggleTheme }}>    
      {children}    
    </ThemeContext.Provider>    
  );    
}    
    
export function useTheme() {    
  return useContext(ThemeContext);    
}    

App.jsThemeToggleコンポーネントを以下のように変更します。

function ThemeToggle() {    
  const { theme, toggleTheme } = useTheme();    
  return (    
    <button onClick={toggleTheme}>    
      {theme === 'light' ? 'ダークモード' : theme === 'dark' ? 'セピアモード' : 'ライトモード'}に切り替え    
    </button>    
  );    
}    

App.cssにセピアモードのスタイルを追加します。

.app-container.sepia {    
  background-color: #f4ecd8;    
  color: #5c4b3a;    
}    
    
.content.sepia {    
  background-color: #e6d7b2;    
  padding: 20px;    
  border-radius: 5px;    
}    
    
nav a.sepia {    
  color: #8b6f47;    
}    

ブラウザで確認すると、ボタンをクリックするたびにライト、ダーク、セピアモードが順に切り替わります。自分で新しいテーマ(例:ハイコントラストモード)やコンテキストで管理するデータ(例:ユーザー設定)を追加し、コンテキストAPIの活用方法を試してください。

次のステップに向けて

本記事では、コンテキストAPIを使用してコンポーネント間でデータを共有し、グローバルな状態管理を実現しました。コンテキストAPIは、アプリケーションのスケーラビリティを向上させる重要なツールです。第11回では、カスタムフックを設計し、再利用可能なロジックを作成する方法を学びます。これにより、コードのモジュール性と保守性がさらに向上します。

おすすめの記事