React

React useContext入門|Propsなしで値を受け渡す

ReactのuseContextは、コンポーネントツリーの深い階層にデータを渡す際に、中間コンポーネントを経由せずに直接値を受け渡しできるフックです。Propsのバケツリレー(Props Drilling)を解消し、テーマ設定やユーザー情報などのグローバルな状態を効率的に共有できます。

基本的な使い方

sample.js
import React, { createContext, useContext, useState } from 'react';

// テーマコンテキストを作成
const ThemeContext = createContext({
  isDarkMode: false,
  toggleTheme: () => {}
});

// テーマプロバイダーコンポーネント
function ThemeProvider({ children }) {
  const [isDarkMode, setIsDarkMode] = useState(false);
  
  const toggleTheme = () => {
    setIsDarkMode(prevMode => !prevMode);
  };
  
  const value = {
    isDarkMode,
    toggleTheme
  };
  
  return (
    <ThemeContext.Provider value={value}>
      {children}
    </ThemeContext.Provider>
  );
}

// テーマボタンコンポーネント
function ThemeToggleButton() {
  const { isDarkMode, toggleTheme } = useContext(ThemeContext);
  
  return (
    <button 
      onClick={toggleTheme}
      style={{
        backgroundColor: isDarkMode ? '#fff' : '#333',
        color: isDarkMode ? '#333' : '#fff',
        padding: '8px 16px',
        border: 'none',
        borderRadius: '4px',
        cursor: 'pointer',
        margin: '10px 0'
      }}
    >
      {isDarkMode ? '🌞 ライトモード' : '🌙 ダークモード'}
    </button>
  );
}

// ヘッダーコンポーネント
function Header() {
  const { isDarkMode } = useContext(ThemeContext);
  
  return (
    <header style={{
      backgroundColor: isDarkMode ? '#222' : '#f0f0f0',
      color: isDarkMode ? '#fff' : '#333',
      padding: '10px 20px',
      borderRadius: '8px 8px 0 0',
      transition: 'all 0.3s ease'
    }}>
      <h1>Context APIデモ</h1>
      <ThemeToggleButton />
    </header>
  );
}

// コンテンツコンポーネント
function Content() {
  const { isDarkMode } = useContext(ThemeContext);
  
  return (
    <main style={{
      backgroundColor: isDarkMode ? '#333' : '#fff',
      color: isDarkMode ? '#fff' : '#333',
      padding: '20px',
      minHeight: '200px',
      transition: 'all 0.3s ease'
    }}>
      <h2>コンテキストの使い方</h2>
      <p>
        このコンポーネントはThemeContextを通じてテーマ情報を受け取っています。
        これにより、propsを介さずにコンポーネントツリーの深い階層にデータを渡すことができます。
      </p>
      <p>
        現在のテーマ: <strong>{isDarkMode ? 'ダークモード' : 'ライトモード'}</strong>
      </p>
    </main>
  );
}

// フッターコンポーネント
function Footer() {
  const { isDarkMode } = useContext(ThemeContext);
  
  return (
    <footer style={{
      backgroundColor: isDarkMode ? '#222' : '#f0f0f0',
      color: isDarkMode ? '#fff' : '#333',
      padding: '10px 20px',
      borderRadius: '0 0 8px 8px',
      textAlign: 'center',
      transition: 'all 0.3s ease'
    }}>
      <p>React Context APIのサンプル</p>
    </footer>
  );
}

// アプリケーションコンポーネント
function ContextApp() {
  return (
    <ThemeProvider>
      <div style={{
        maxWidth: '600px',
        margin: '0 auto',
        fontFamily: 'Arial, sans-serif',
        boxShadow: '0 0 10px rgba(0,0,0,0.1)',
        borderRadius: '8px',
        overflow: 'hidden'
      }}>
        <Header />
        <Content />
        <Footer />
      </div>
    </ThemeProvider>
  );
}

export default ContextApp;

説明

Step 1Reactのコンテキスト(Context)とは

Reactのコンテキスト(Context)は、propsを介さずにコンポーネントツリーを通してデータを直接受け渡しできる機能です。これはprops drilling(深い階層へのprops受け渡し)の問題を解決します。

コンテキストが役立つケース:

  • テーマ設定(ダークモード/ライトモードなど)
  • ユーザー認証情報
  • 言語設定や地域設定
  • 複数のコンポーネントで共有する状態

Step 2コンテキストの作成

まず、React.createContextを使ってコンテキストを作成します:

// ThemeContext.js import React, { createContext, useState } from 'react'; // コンテキストを作成(デフォルト値を設定可能) export const ThemeContext = createContext({ isDarkMode: false, toggleTheme: () => {}, }); // プロバイダーコンポーネントを作成 export const ThemeProvider = ({ children }) => { const [isDarkMode, setIsDarkMode] = useState(false); // テーマ切り替え関数 const toggleTheme = () => { setIsDarkMode(prevMode => !prevMode); }; // コンテキスト値 const value = { isDarkMode, toggleTheme }; return ( <ThemeContext.Provider value={value}> {children} </ThemeContext.Provider> ); };

Step 3コンテキストプロバイダーの設定

作成したプロバイダーを、データを共有したいコンポーネントの上位に配置します:

// App.js import React from 'react'; import { ThemeProvider } from './ThemeContext'; import MainContent from './MainContent'; function App() { return ( <ThemeProvider> <div className="App"> <h1>Context APIのデモ</h1> <MainContent /> </div> </ThemeProvider> ); }

Step 4useContextフックでコンテキストの値を使用する

子孫コンポーネントでuseContextフックを使用して、コンテキストの値にアクセスします:

// ThemeButton.js import React, { useContext } from 'react'; import { ThemeContext } from './ThemeContext'; function ThemeButton() { // コンテキストから値を取得 const { isDarkMode, toggleTheme } = useContext(ThemeContext); return ( <button onClick={toggleTheme} style={{ backgroundColor: isDarkMode ? '#333' : '#f0f0f0', color: isDarkMode ? '#fff' : '#000', padding: '10px 15px', border: 'none', borderRadius: '4px', cursor: 'pointer' }} > {isDarkMode ? 'ライトモードに切り替え' : 'ダークモードに切り替え'} </button> ); }
// ThemedComponent.js import React, { useContext } from 'react'; import { ThemeContext } from './ThemeContext'; import ThemeButton from './ThemeButton'; function ThemedComponent() { const { isDarkMode } = useContext(ThemeContext); return ( <div style={{ backgroundColor: isDarkMode ? '#333' : '#fff', color: isDarkMode ? '#fff' : '#333', padding: '20px', borderRadius: '8px', transition: 'all 0.3s ease' }} > <h2>テーマ切り替えのデモ</h2> <p>現在のテーマ: {isDarkMode ? 'ダークモード' : 'ライトモード'}</p> <ThemeButton /> </div> ); }

Step 5複数のコンテキストを使用する

アプリケーションでは、複数のコンテキストを組み合わせて使用することもできます:

// App.js import React from 'react'; import { ThemeProvider } from './ThemeContext'; import { UserProvider } from './UserContext'; import MainContent from './MainContent'; function App() { return ( <ThemeProvider> <UserProvider> <div className="App"> <h1>複数のContextを使用</h1> <MainContent /> </div> </UserProvider> </ThemeProvider> ); }

そして、コンポーネントで複数のコンテキストを利用できます:

import React, { useContext } from 'react'; import { ThemeContext } from './ThemeContext'; import { UserContext } from './UserContext'; function ProfileCard() { const { isDarkMode } = useContext(ThemeContext); const { user } = useContext(UserContext); return ( <div style={{ backgroundColor: isDarkMode ? '#333' : '#fff' }}> <h2>プロフィール</h2> <p>ユーザー名: {user.name}</p> <p>メール: {user.email}</p> </div> ); }
ポイント

重要: コンテキストは便利ですが、過剰に使用するとコンポーネントの再利用性や保守性が低下する場合があります。グローバルな状態管理が必要ない場合は、通常のpropsでの値の受け渡しを検討してください。

また、頻繁に変更される値をコンテキストで提供すると、多くのコンポーネントが再レンダリングされる可能性があるため、パフォーマンスに注意が必要です。必要に応じてメモ化(React.memo、useMemo、useCallback)を検討してください。

まとめ

  • createContext()でコンテキストを作成し、Providerで値を提供する
  • useContext()フックでコンテキストの値を取得できる
  • Props Drilling(バケツリレー)を解消し、離れたコンポーネント間でデータを共有できる
  • テーマ設定・ユーザー認証情報・言語設定など、グローバルな状態の管理に適している
  • Providerの値が変更されると、そのコンテキストを使用するすべてのコンポーネントが再レンダリングされる
  • 頻繁に変更される状態には、パフォーマンスを考慮してコンテキストを分割するとよい