５分でわかる useContext の使い方【TypeScriptまで】

できるだけシンプルに useContext の使い方を解説。

## useContextとはそもそも何
useContext とは親より全ての子(孫)がプロパティや値をグローバルに扱えるようにする Hook（または親以下をスコープにするHookとも言える）。
したがって、親に埋め込まれていないコンポーネントへは値を渡すことはできない。

useState と合わせて使うことで props のバケツリレーをせずに状態を更新できるようになるのがメリット。
バケツリレーの複雑さを避けるために使えるかと。

## 使い方
1. createContext() で親に初期値を渡す(createContextをimport)
2. useContext() で子や孫で受け取る(useContextをimport)

## 単純なサンプル
Parent.js
Child.js
ChildChild.js
ChildChildChild.js
で親から孫のファイルを作った。ネストさせてコンポートネントを埋め込んでいる。
まずは useState などは使わずに使ってみることに。

Parent.js では createContext で初期値を設定している。宣言した 変数Context がグローバルで扱える変数となる。

```Parent.js
import React, { createContext } from "react";
import Child from "./Child";

export const Context = createContext("私はContextです。propsで渡してもらっていません。");

const Parent = () => (
    <>
      <h1>親です</h1>
      <Child />
    </>
 );

export default Parent;

```

Child.js以降では useContext(Context) で 親の createContext を使って宣言した 変数Context を呼び出すことができる。

```Child.js
import React, { useContext } from "react";
import ChildChild from "./ChildChild";
import { Context } from "./Parent";

const Child = () => {
  const ChildContext = useContext(Context);
  return (
    <>
      <h2>Childです</h2>
      <p>{ChildContext}</p>
      <ChildChild />
    </>
  );
};
export default Child;

```

```ChildChild.js
import React, { useContext } from "react";
import ChildChildChild from "./ChildChildChild";
import { Context } from "./Parent";

const ChildChild = () => {
  const ChildChildContext = useContext(Context);

  return (
    <>
      <h2>ChildChildです</h2>
      <p>{ChildChildContext}</p>
      <ChildChildChild />
    </>
  );
};

export default ChildChild;

```

```ChildChildChild.js
import React, { useContext } from "react";
import { Context } from "./Parent";

const ChildChildChild = () => {
  const ChildChildChildContext = useContext(Context);

  return (
    <>
      <h2>ChildChildChildです</h2>
      <p>{ChildChildChildContext}</p>
    </>
  );
};

export default ChildChildChild;
```
このようにpropsを使わずに値を呼び出すことができた。


## useContextの値の更新
今度は先ほどのサンプルを元に useState と合わせて使う。
といっても useState で状態を設定したら親側に Context.Provider というタグを埋め込みそこに state を設定するだけ。

codesandbox: https://codesandbox.io/s/weathered-dawn-buyky?fontsize=14&hidenavigation=1&theme=dark

```Parent.js
import React, { useState, createContext } from "react";
import Child from "./Child";

export const Context = createContext();

const Parent = () => {
  const [state, setState] = useState(
    "私はContextです。propsで渡してもらっていません。"
  );
  const updateContext = () => setState("Contextを更新したよ。");

  return (
    <>
      <h1>親です</h1>
      <button onClick={updateContext}>contextを更新するボタン</button>
      <Context.Provider value={state}>
        <Child />
      </Context.Provider>
    </>
  );
};

export default Parent;

```

```Parent.js
      <button onClick={updateContext}>contextを更新するボタン</button>
      <Context.Provider value={state}>
        <Child />
      </Context.Provider>
```
更新ボタンで state を更新させる。
Context.Provider のタグは子コンポーネントを囲ってあげる。

Provider で設定された value が親より以下コンポーネントが参照する useContext の値となるのでそこにstateを設定すれば当然、状態を親以下のスコープで更新することができるようになる。

##TypeScriptでuseContextを使う
こちらもcodesandboxでサンプルを用意した
（https://codesandbox.io/s/optimistic-sea-x9c95?fontsize=14&hidenavigation=1&theme=dark）

```Parent.tsx
import React, { FC, createContext, useState } from "react";
import Child from "./Child";

export type ContextType = string;
export const Context = createContext<ContextType>("");

const Parent: FC = () => {
  const [state, setState] = useState(
    "私はContextです。propsで渡してもらっていません。"
  );
  const updateContext = () => setState("Contextを更新したよ。");

  return (
    <>
      <h1>親です</h1>
      <button onClick={updateContext}>contextを更新するボタン</button>
      <Context.Provider value={state}>
        <Child />
      </Context.Provider>
    </>
  );
};

export default Parent;

```

Context を宣言するタイミングで型を付けてあげる

```Parent.tsx
export type ContextType = string;
export const Context = createContext<ContextType>("");
```
type ContextType = string; と解説用に用意しているがオブジェクトなどの型を用意しておき、createContext と共に型を当てはめておきたい。

### TypeScriptのuseContextの蛇足

```
export const Context = createContext<Partial<ContextType>>({});
```
蛇足として使ったことはないものの、オブジェクトを初期化する場合は Partial を使うことで初期化できるとのこと。
参考：<a href="https://www.fullstacklabs.co/blog/usecontext-data-discoverable-typescript">Make useContext Data More Discoverable with Typescript</a>

## まとめてみた所感
useState との兼ね合いがわかりにくかったが記事において useContext 単体での動きと切り分けてまとめてみる比較的にシンプルな Hook だと思った。
あくまで useContext(createContex) はプロパティや値のスコープ拡張をするもの。