React useContext()を利用してデータを渡す

# useContext
`useContext()`とは、ツリー内の様々な階層から`Context`に収容されているデータへアクセスすることができます。`useContext()`を利用することで親コンポーネントから子コンポーネント、子コンポーネントから孫コンポーネントへ`prpos`で値を渡さなくても、直接親コンポーネントから孫コンポーネントへ値を渡すことができます。

# useContextを利用するために必要なコンポーネント
`Context`機能を利用するためには以下のコンポーネントが必要になります。
| オブジェクト/コンポーネント | 説明 |
|:-:|:-:|
| Contextオブジェクト  | React.createContext()の戻り値  |
| Providerコンポーネント  | Contextオブジェクトが保持しているコンポーネント  |
| Consumerコンポーネント  | useContext()を利用することで、Contextオブジェクトから値を取得できるコンポーネント  |

# useContextを使用したサンプル1
createContext()を利用して`Provider`コンポーネントである`SampleProvider`コンポーネントと`Context`オブジェクトである`SampleObject`を作成します。

https://codesandbox.io/embed/usecontext-arenzi-hrr5xo?fontsize=14&hidenavigation=1&theme=dark

## index.js
```javascript:index.js
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";

import App from "./App";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <StrictMode>
    <App />
  </StrictMode>
);
```

## SampleProvider.js
```javascript:SampleProvider.js
// 関数コンポーネント内でceateContextを扱えるようにするためimportする
import React, { createContext } from "react";

// createContext()を利用して、ContextオブジェクトSampleObjectを作成
// 他のファイルでSampleObjectを参照できるようexportする
export const SampleObject = createContext();

// 子コンポーネント（下階層）に渡すtextを定義する
const text = "これはProviderから渡された値";

// 定義したtextの情報を保持するSampleObjcet.Providerを作成する
// SampleObject.Providerでprops.childrenをラップしておくと
// ラップしたコンポーネントのツリー内でContextオブジェクトを
// 参照できるようなるchildrenをpropsで受け取る
export const SampleProvider = (props) => {
  return (

    // 共有したい値をvalue属性の値として渡す
    // value属性の値は、SampleProviderコンポーネントでラップした
    // コンポーネントのツリー内で参照できるようになる
    <SampleObject.Provider value={text}>{props.children}</SampleObject.Provider>
  );
};
```

## App.js
```javascript:App.js
// SampleProviderコンポーネントをインポートする
import { SampleProvider } from "./SampleProvider";

// Firstコンポーネントをインポートする
import { First } from "./First";

export default function App() {
  return (
    <>
      // SampleProviderでラップしているので、ラップされたFirstコンポーネントの配下で
      // Contextオブジェクトを参照できるようになる
      // Firstコンポーネント配下のコンポーネント（Second、Third）は、Provider内なので、
      // value属性の参照できる
      <SampleProvider>
        <First />
      </SampleProvider>
    </>
  );
}
```

<br>

今回、Contextオブジェクトを保持しているProviderコンポーネンをSampleProviderコンポーネントとして別ファイルに記述していましたが、以下のようにApp.jsに一つにまとめて書くことができます。

```javascript:App.js
// 関数コンポーネント内でceateContextを扱えるようにするためimportする
import React, { createContext } from "react";

// createContext()を利用して、ContextオブジェクトSampleObjectを作成
// 他のファイルでSampleObjectを参照できるようexportする
export const SampleObject = createContext();

// 子コンポーネント（下階層）に渡すtextを定義する
const text = "これはProviderから渡された値";

// Firstコンポーネントをインポートする
import { First } from "./First";

export default function App() {
  return (
    <>
      
      // SampleObject.Providerでラップするとラップすると、
      // ラップされたコンポーネントのツリー内（First、Second、Third）de
      // value属性の値を参照できるようになる
      <SampleObject.Provider value={text}>

        // Firstコンポーネントは、Provider内なので、value属性の値を参照できる
        <First />
      </SampleObject.Provider>
    </>
  );
}
```

## First.js
```javascript:First.js
// Secondコンポーネントをインポート
import { Second } from "./Second";

// 他のファイルでFirstコンポーネントを使用できるようexportする
export const First = () => {
  return (
    <>
      <p>Firstコンポーネント</p>
      <Second />
    </>
  );
};
```

## Second.js
```javascript:Second.js
// Thirdコンポーネントをインポート
import { Third } from "./Third";

// 他のファイルでSecondコンポーネントを使用できるようexportする
export const Second = () => {
  return (
    <>
      <p>Secondコンポーネント</p>
      <Third />
    </>
  );
};
```

## Third.js
```javascript:Third.js
// 関数コンポーネント内でuseContextを使用できるようインポートする
import React, { useContext } from "react";

// ContextオブジェクトのSampleObjectをインポートする
import { SampleObject } from "./SampleProvider";

// 他のファイルでThirdコンポーネントを使用できるようexportする
// Thirdコンポーネントは、Contextオブジェクトから値を取得できるConsumerコンポーネント
export const Third = () => {

  // useContext（）を使用して、Contextオブジェクトから値を取得
  const sampleText = useContext(SampleObject);

  return (
    <>
      <p>
        // sampleTextはProviderコンポーネントから取得したvalueの値
        Thirdコンポーネント: <b>{sampleText}</b>
      </p>
    </>
  );
};
```

# useContextを使用したサンプル2
`Provider`コンポーネントから`useState()`を使用して保持している値`count`を受け取ります。

https://codesandbox.io/embed/usecontext-arenzi2-rkx8xc?fontsize=14&hidenavigation=1&theme=dark

## index.js
```javascript:index.js
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";

import App from "./App";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <StrictMode>
    <App />
  </StrictMode>
);
```

## CountProvider
```javascript:CountProvider.js
// useState、createContextを使用できるようにインポートする
import React, { useState, createContext } from "react";

// 他のファイルでCountObjectを使用できるようにするためにexportする
// createContext()を使用して、ContextオブジェクトCountObjectを作成
export const CountObject = createContext();

// useStateで定義したcount/setCount情報を保持する
// CountObject.Providerを作成し、props.childrenをラップしておくと、
// CountProviderコンポーネントでラップしたコンポーネントのツリー内で
// Contextオブジェクトを参照できるようになる
// 今回の場合、propsで受け取る値はFirstコンポーネント
export const CountProvider = (props) => {

  // 状態変数count、countを更新する関数setCountを定義する
  const [count, setCount] = useState(0);

  return (

    // value属性の値は、CountProviderコンポーネントでラップした
    // コンポーネントのツリー内（First、Second、Third）で参照できるようになる
    <CountObject.Provider value={[count, setCount]}>
      {props.children}
    </CountObject.Provider>
  );
};
```

## App.js
```javascript:App.js
// CountProviderコンポーネントをインポートする
import {CountProvider} from "./CountProvider";

// Firstコンポーネントをインポートする
import {First} from "./First";

export default function App() {
  return (
    <>
      // CountProviderでラップしているので、ラップされたFisrtコンポーネントの配下で、
      // Contextオブジェクトの値を参照できるようになる
      // Firstコンポーネント配下のコンポーネントは、Provider内なので、value属性の値を参照できる
      <CountProvider>
        <First />
      </CountProvider>
    </>
  );
}
```

## First.js
```javascript:First.js
// Secondコンポーネントをインポート
import { Second } from "./Second";

// 他のファイルでFirstコンポーネントを使用できるようexportする
export const First = () => {
  return (
    <>
      <p>Firstコンポーネント</p>
      <Second />
    </>
  );
};
```

## Second.js
```javascript:Second.js
// Thirdコンポーネントをインポート
import { Third } from "./Third";

// 他のファイルでSecondコンポーネントを使用できるようexportする
export const Second = () => {
  return (
    <>
      <p>Secondコンポーネント</p>
      <Third />
    </>
  );
};
```

## Third.js
```javascript:Third.js
// useContextを使用できるようインポートする
import React, { useContext } from "react";

// ContextオブジェクトのCountObjectをインポートする
import { CountObject } from "./CountProvider";

// 他のファイルでThirdコンポーネントを利用できるようにするためにexport
// Thirdコンポーネントは、Contextオブジェクトから値を取得できるConsumerコンポーネント
export const Third = () => {

  // useContext（）を使用してContextオブジェクトから値を取得
  // [count, setCount]はProviderコンポーネントから取得したvalueの値
  const [count, setCount] = useContext(CountObject);

  // カウントアップする関数countIncrement
  const countIncrement = () => {
    setCount((prevCount) => prevCount + 1);
  };

  // カウントダウンする関数countDecrement
  const countDecrement = () => {
    setCount((prevCount) => prevCount - 1);
  };

  // カウントリセットする関数countReset
  const countReset = () => {
    setCount(0);
  };

  return (
    <>
      <p>
        Thirdコンポーネント:<b>現在のカウント: {count}</b>
      </p>
      <button onClick={countIncrement}>+1ボタン</button>
      <button onClick={countDecrement}>-1ボタン</button>
      <button onClick={countReset}>リセットボタン</button>
    </>
  );
};
```