できるだけシンプルに useContext の使い方を解説。
useContextとはそもそも何
useContext とは親より全ての子(孫)がプロパティや値をグローバルに扱えるようにする Hook(または親以下をスコープにするHookとも言える)。
したがって、親に埋め込まれていないコンポーネントへは値を渡すことはできない。
useState と合わせて使うことで props のバケツリレーをせずに状態を更新できるようになるのがメリット。
バケツリレーの複雑さを避けるために使えるかと。
使い方
- createContext() で親に初期値を渡す(createContextをimport)
- useContext() で子や孫で受け取る(useContextをimport)
単純なサンプル
Parent.js
Child.js
ChildChild.js
ChildChildChild.js
で親から孫のファイルを作った。ネストさせてコンポートネントを埋め込んでいる。
まずは useState などは使わずに使ってみることに。
Parent.js では createContext で初期値を設定している。宣言した 変数Context がグローバルで扱える変数となる。
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 を呼び出すことができる。
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;
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;
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
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;
<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)
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 を宣言するタイミングで型を付けてあげる
export type ContextType = string;
export const Context = createContext<ContextType>("");
type ContextType = string; と解説用に用意しているがオブジェクトなどの型を用意しておき、createContext と共に型を当てはめておきたい。
TypeScriptのuseContextの蛇足
export const Context = createContext<Partial<ContextType>>({});
蛇足として使ったことはないものの、オブジェクトを初期化する場合は Partial を使うことで初期化できるとのこと。
参考:Make useContext Data More Discoverable with Typescript
まとめてみた所感
useState との兼ね合いがわかりにくかったが記事において useContext 単体での動きと切り分けてまとめてみる比較的にシンプルな Hook だと思った。
あくまで useContext(createContex) はプロパティや値のスコープ拡張をするもの。