Reactを基本からまとめてみた【18】【React Hook (useContext) ③】

##useContextはどういう時に使用するか
コンポーネントをまたいで値を渡す時。

propsで渡すと孫コンポーネントに渡す時に、一度、子や孫コンポーネントを経由しなければならなくなりバケツリレーになる。(propドリル)

さらにContextのいい点はContextで管理している状態や関数をそれぞれの子コンポーネントで共通して扱えるということ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/a667324a-4c19-8df5-3b44-4ea627e814e3.png)

##Contextの概念図
以下の図は、AuthContextという認証関連の変数や操作ロジックを扱ったContextと、それを実際に使用する2つのコンポーネントとの関係性を図示したもの。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/939638/b040a2a7-d575-827e-19b1-d7feabeee419.png)

:::note info
・ログインしているかどうかを示す変数であるisAuthがAuthContextで一元的に管理されている
・この子コンポーネントはisAuthを使えるようになり、isAuthの値に応じた処理を書くことができるようになる
・コンポーネントAではloginHandlerを呼び出すことで、AuthContext内のisAuthをtrueに変更することができる
:::

##Contextの使い方

今回はこういうファイル構成にしてComponentAとComponentBでそれぞれ共通の変数を扱うようにする。
実際にはありえないがまぁ今回はよしとする。

├── App.jsx
├── AuthContext.jsx
├── ComponentA.jsx
└── ComponentB.jsx

##Step 1.Contextの作成

まずは子コンポーネントで使う変数や関数を管理するためのContextを作成する。
ここではAuthContextを作成する。

#####ポイントは以下の2つ

:::note info
①createContextでContextオブジェクトを作成し、外部で使えるようにexportする
②子コンポーネントをContextオブジェクトのProviderでラップすることでvalueに記載した変数や関数を子コンポーネントで使用できるようにする
:::

```AuthContext.jsx
import React, { useState, createContext } from 'react';

// Contextオブジェクトを作成し、exportする
export const AuthContext = createContext();

const AuthContextProvider = props => {
  const [isAuth, setIsAuth] = useState(false);

  const login = () => {
    setIsAuth(true);
  }

  return (
    // Providerで子コンポーネントをラップする
    // valueに子コンポーネントで使いたい変数や関数を与える
    <AuthContext.Provider value={{login, isAuth}}>
      {props.children}
    </AuthContext.Provider>
  )
}

export default AuthContextProvider;
```

createContext は Contextオブジェクトを作り出していて、これによって Provider とConsumer ができる。
Provider は与える側、Consumer は受け取る側。
hooks ではConsumer は useContext で代用される。

##Step 2.Providerを適切な位置に配置する

```App.jsx
import { ComponentA } from "./ComponentA";
import { ComponentB } from "./ComponentB";
import AuthContextProvider from "./AuthContext";

function App() {
  return (
    <BrowserRouter>
      <AuthContextProvider>
        <Switch>
          <Route path="/a" component={ComponentA} />
          <Route path="/b" component={ComponentB} />
        </Switch>
      </AuthContextProvider>
    </BrowserRouter>
  );
}

export default App;
```
子コンポーネントであるComponentAとComponentBではStep1のAuthContextProviderで定義した変数と関数を使える準備ができたということになる。

##Step 3.子コンポーネントでContextを使う

#####ComponentA

:::note info
①useContextをimoprt
②useContextの引数にAuthContextで作成したContextオブジェクト(Step 1で作成)を入れる
③使いたいオブジェクトを取り出す(ここではisAuthとlogin)
:::

```ComponentA.jsx
import React, { useContext } from "react";
import { AuthContext } from "./AuthContext";
import { Link } from "react-router-dom";

const ComponentA = () => {
  const { isAuth, login } = useContext(AuthContext);

  return (
    <div className="auth">
      <h2>ComponentA</h2>
      {isAuth ? (
        <h2>認証されました</h2>
      ) : (
        <>
          <h2>認証されてません</h2>
          <button onClick={login}>ログイン</button>
        </>
      )}
      <Link to="/b">ComponentBへ</Link>
    </div>
  );
};

export default ComponentA;
```

ここではisAuthの真偽によって表示する内容を変えている。
さらにonClickイベントでログイン関数を呼び出している。

####ComponentB

```ComponentB.jsx
import React, { useContext } from "react";
import { AuthContext } from "./AuthContext";

const ComponentB = () => {
  const { isAuth } = useContext(AuthContext);

  return (
    <div>
      <h2>ComponentB</h2>
      {isAuth ? <h2>認証されました</h2> : <h2>認証されてません</h2>}
      <Link to="/a">ComponentAへ</Link>
    </div>
  );
};

export default ComponentB;

```
ComponentBも同様にisAuthを取り出して表示内容を変えている。

##Contextによって何ができるようになったのか

:::note info
①ComponentAでログインボタンを押すことでlogin関数が呼び出される
②AuthContextのisAuthがfalseからtrueに変更される
③ComponentA, ComponentBでそれぞれ変更されたisAuthの値を使えるようになった
:::

####つまりComponentAによる値の変更をComponentBで検知できるようになった。
##参考サイト
[useContextの使い方]
(https://www.sunapro.com/usecontext/)