React hooks【useContext】
useContextを使用することでグローバルな状態を作成し、コンポーネントツリーのどこからでもコンテキストの値を簡単に取得できる。通常、親コンポーネントから子コンポーネントへpropsを渡す必要があるが、useContextを使うことで、深い階層のコンポーネントにも直接データを提供できる。これにより、props を何階層も渡す 「props drilling」、いわゆるバケツリレーを避けられる。
useContext の基本概念
- Context API: React の組み込み機能で、アプリケーション内でデータを グローバルに共有 するための仕組み。
- useContext: コンポーネント内で、Context で提供されたデータにアクセスするためのフック。
使用例 (モードの切り替え機能)
フォルダ構成
/src
├── components
│ ├── Parent.jsx
│ ├── Child.jsx
│ ├── GrandChild.jsx
├── context
│ ├── ThemeContext.jsx ← Context を定義
├── App.jsx
1:ThemeContext.jsx
import { createContext, useContext, useState } from "react";
// Context を作成
const ThemeContext = createContext();
// Provider コンポーネントを作成
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState("light");
const toggleTheme = () => {
setTheme(theme === "light" ? "dark" : "light");
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
// ThemeContext をエクスポート
export { ThemeContext };
2:App.jsx
import { ThemeProvider } from "./context/ThemeContext";
import Parent from "./components/Parent";
function App() {
return (
<ThemeProvider>
<Parent /> // 親コンポーネント呼び出し
</ThemeProvider>
);
}
export default App;
3:Parent.jsx
import Child from "./Child";
function Parent() {
return <Child />; // 子コンポーネント呼び出し。useContext のおかげで props を渡す必要がない
}
export default Parent;
4:Child.jsx
import GrandChild from "./GrandChild";
function Child() {
return <GrandChild />; // 孫コンポーネント呼び出し。props を中継せずに済む
}
export default Child;
5:GrandChild.jsx
import { useContext } from "react";
import { ThemeContext } from "../context/ThemeContext";
function GrandChild() {
// useContext で直接テーマと切り替え関数を取得
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<div style={{ background: theme === "light" ? "#fff" : "#333", color: theme === "light" ? "#000" : "#fff", padding: "20px" }}>
<p>現在のテーマ: {theme}</p>
<button onClick={toggleTheme}>テーマを切り替え</button>
</div>
);
}
export default GrandChild;
上記コードでは、親コンポーネントである Parent.jsx
から props のバケツリレーを行わずに孫コンポーネントの GrandChild.jsx
で Context にアクセスすることができ、テーマの変更処理を行うことができる。使用しなかった場合は以下のようになると考えられる。
useContext を使用しない場合の例
以下は、useContext
を使わず props でデータを伝播する例。フォルダ構成は useContext
使用時と同じ(/src/components/
配下に Parent.jsx
, Child.jsx
, GrandChild.jsx
)を前提とする。
1:App.jsx (useContext不使用)
import { useState } from "react";
import Parent from "./components/Parent";
function App() {
const [theme, setTheme] = useState("light");
const toggleTheme = () => {
setTheme(theme === "light" ? "dark" : "light");
};
return (
<Parent theme={theme} toggleTheme={toggleTheme} />
);
}
export default App;
2:Parent.jsx (useContext不使用)
import Child from "./Child";
function Parent({ theme, toggleTheme }) {
return <Child theme={theme} toggleTheme={toggleTheme} />;
}
export default Parent;
3:Child.jsx (useContext不使用)
import GrandChild from "./GrandChild";
function Child({ theme, toggleTheme }) {
return <GrandChild theme={theme} toggleTheme={toggleTheme} />;
}
export default Child;
4:GrandChild.jsx (useContext不使用)
function GrandChild({ theme, toggleTheme }) {
return (
<div style={{ background: theme === "light" ? "#fff" : "#333", color: theme === "light" ? "#000" : "#fff", padding: "20px" }}>
<p>現在のテーマ: {theme}</p>
<button onClick={toggleTheme}>テーマを切り替え</button>
</div>
);
}
export default GrandChild;
useContext
を使用せずに、props を使ってテーマの状態を親コンポーネントから子・孫コンポーネントに伝播しているが、コンポーネント間でのデータの受け渡しが煩雑になる。
このように useContext
を使用することでコンポーネントの管理がしやすく、コード量が減り、再利用性が高く、シンプルで効率的な状態共有が可能となる。
アプリケーション全体で共通の情報(テーマや認証情報など)を管理する場合に非常に有効。
補足
Context に渡すデータが変化すると、そのデータを利用しているすべてのコンポーネントが再レンダリングされるため、頻繁に変更されるデータを Context に渡すとパフォーマンスの問題が発生する可能性があるため注意。
対策:
- コンテキストの分割: 頻繁に更新されるデータとそうでないデータを別の Context に分ける。
-
メモ化:
useMemo
やuseCallback
を使って、Context の値や関数が不必要に再生成されないようにする。
例:const value = useMemo(() => ({ theme, toggleTheme }), [theme]); return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
これにより、不要な再レンダリングを減らせる。