【React学習記録】 useContext

## 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
```

#### １：ThemeContext.jsx
```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 };
```

#### ２：App.jsx
```jsx
import { ThemeProvider } from "./context/ThemeContext";
import Parent from "./components/Parent";

function App() {
  return (
    <ThemeProvider>
      <Parent /> // 親コンポーネント呼び出し
    </ThemeProvider>
  );
}

export default App;
```

#### ３：Parent.jsx
```jsx
import Child from "./Child";

function Parent() {
  return <Child />; // 子コンポーネント呼び出し。useContext のおかげで props を渡す必要がない
}

export default Parent;
```

#### ４：Child.jsx
```jsx
import GrandChild from "./GrandChild";

function Child() {
  return <GrandChild />; // 孫コンポーネント呼び出し。props を中継せずに済む
}

export default Child;
```

#### ５：GrandChild.jsx
```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`）を前提とする。

#### １：App.jsx (useContext不使用)
```jsx
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;
```

#### ２：Parent.jsx (useContext不使用)
```jsx
import Child from "./Child";

function Parent({ theme, toggleTheme }) {
  return <Child theme={theme} toggleTheme={toggleTheme} />;
}

export default Parent;
```

#### ３：Child.jsx (useContext不使用)
```jsx
import GrandChild from "./GrandChild";

function Child({ theme, toggleTheme }) {
  return <GrandChild theme={theme} toggleTheme={toggleTheme} />;
}

export default Child;
```

#### ４：GrandChild.jsx (useContext不使用)
```jsx
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 の値や関数が不必要に再生成されないようにする。  
  例:
  ```jsx
  const value = useMemo(() => ({ theme, toggleTheme }), [theme]);
  return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
  ```
これにより、不要な再レンダリングを減らせる。