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の値が変更されると、そのコンテキストを使用するすべてのコンポーネントが再レンダリングされる
- 頻繁に変更される状態には、パフォーマンスを考慮してコンテキストを分割するとよい