useContextとは
React Hooksの一つで、グローバルにデータを管理することができる仕組みです。
通常親コンポーネントから子コンポーネントにデータを渡す際はprops
を介して行うかと思います。
しかし親から子、そのまた子といったように複数のコンポーネントを介してデータを渡す場合にpropsでのバケツリレーのようなやり方では設定が複雑になってきます。
useContext
を使用することでprops
を利用することなく異なる階層のコンポーネントとデータの共有を行うことができます。
グローバルに「データ」を管理することができるもの。
「props」によるバケツリレーを防ぐために利用する。
シンプルなコード例
下記のようなディレクトリ構造でuseContextを使用してみます。
├ Example.js
├ components/
├ Child.js
├ GrandChild.js
import Child from "./components/Child";
import { createContext } from "react";
export const MyContext = createContext("useContextのテスト");
const Example = () => {
return <Child />;
};
export default Example;
import GrandChild from "./GrandChild";
const Child = () => (
<div style={{ border: "1px solid black", padding: 10 }}>
<h3>子コンポーネント</h3>
<GrandChild />
</div>
);
export default Child;
import { useContext } from "react";
import { MyContext } from "../Example";
const GrandChild = () => {
const value = useContext(MyContext);
return (
<div style={{ border: "1px solid black" }}>
<h3>孫コンポーネント</h3>
{value}
</div>
);
};
export default GrandChild;
上記関数コンポーネントを実行していただくと下記のようにprops
を使用することなく、孫コンポーネントにデータを渡せていることがわかるかと思います。
コード解説
useContext
を使用するには、reactからcreateContext()
を読み込む必要があります。
import { createContext } from "react";
そのcreateContext()
を使用し、コンテキストを定義し、export
します。
createContext()
の引数にはprops
を通さずに渡したい値を記述します。
export const MyContext = createContext("useContextのテスト");
そしてGrandChild
コンポーネントで使用したいので、import
しています。
ここでuseContext()
をreact
から読み込みます。
import { useContext } from "react";
import { MyContext } from "../Example";
最後にuseContext()
の引数にimport
したMyContextを渡すことで、設定しておいた値を取得することができます。
const value = useContext(MyContext);
// value = useContextのテスト
stateとuseContextを組み合わせて使う方法
次にネスト構造になっていない、コンポーネント間でクリックを検知して、state
を変更する方法について解説します。
下記のような構造で作成します。
├ Example.js
├ components/
├ Child.js
├ GrandChild.js
├ Other.js
Otherコンポーネントの兄弟に、Childコンポーネントがあり、そのChildコンポーネントの中にはGrandChildコンポーネントがあるような構造です。
Otherコンポーネントのクリックを検知し、GrandChirdにあるstate
を変更させます。
import { createContext, useState } from "react";
import Child from "./components/Child";
import OtherChild from "./components/OtherChild";
export const MyContext = createContext();
const Example = () => {
const [value, setValue] = useState(0);
return (
<MyContext.Provider value={[value, setValue]}>
<Child />
<OtherChild />
</MyContext.Provider>
);
};
export default Example;
import { useContext } from "react";
import { MyContext } from "../Example";
const OtherChild = () => {
const [, setValue] = useContext(MyContext);
const clickHandler = (e) => {
setValue((prev) => prev + 1);
};
return (
<div>
<h3>他の子コンポーネント</h3>
<button onClick={clickHandler}>+</button>
</div>
);
};
export default OtherChild;
import GrandChild from "./GrandChild";
const Child = () => (
<div style={{ border: "1px solid black", padding: 10 }}>
<h3>子コンポーネント</h3>
<GrandChild />
</div>
);
export default Child;
import { useContext } from "react";
import { MyContext } from "../Example";
const GrandChild = () => {
const [value] = useContext(MyContext);
return (
<div style={{ border: "1px solid black" }}>
<h3>孫コンポーネント</h3>
{value}
</div>
);
};
export default GrandChild;
上記コンポーネントを実行すると、下記のように他のコンポーネントで作成したボタンをクリックすると、孫コンポーネントにあるstate(カウント)
が変化するようになります。
コード解説
まず、state
を使用するコンポーネントを全て読み込んでいる場所でuseState
やcontext
を定義します。
import { createContext, useState } from "react";
import Child from "./components/Child";
import OtherChild from "./components/OtherChild";
export const MyContext = createContext(); // context定義
const Example = () => {
const [value, setValue] = useState(0); // state定義
...
};
定義したcontext
(MyContext)のProviderというコンポーネントでstateを使用するコンポーネントを包装します。
そのProvider
コンポーネントのvalue
を設定することで、value
にセットされた値がuseContext
を通してネストされたコンポーネントで取得することができるようになります。
今回はvalue
に設定する値は定義しておいた、stateとその変更関数ごと、配列のまま渡します。
<MyContext.Provider value={[value, setValue]}>
<Child />
<OtherChild />
</MyContext.Provider>
useContextの挙動とすると順々に親のコンポーネントを参照していき、Provider
コンポーネントのvalue
に設定された値を取得します。
Provider
コンポーネントのvalue
を設定しなかった場合、createContext()
時に渡された引数を取得します。
例)
export const MyContext = createContext('これが初期の値になるよ');
ボタンが作成されているOther.jsではcontext
をimportし、useContext()
の引数に渡します。
import { useContext } from "react";
import { MyContext } from "../Example"; // 定義したMyContextのimport
それをuseContext()
の引数に渡し、返り値を格納することでvalue
に設定された値を取得できます。
value
には[value, setValue]
の配列を渡してありますが、このコンポーネントではstate
は使いませんので、分割代入で1番目の要素であるsetValue
、つまりstateの変更関数だけを取得します。
const [, setValue] = useContext(MyContext); // 使用する変更関数のみ取得
そしてボタンのonClickイベントでclickHandler
を実行させます。
clickHandler()
はsetValue
でstate
の値に+1をする関数です。
const clickHandler = (e) => {
setValue((prev) => prev + 1);
};
stateを表示しているGrandChild.jsも同じ要領で、useContext()
の引数にcontext
、つまりvalue
に設定したstate
の値のみ取得して表示させているという流れです。
import { useContext } from "react";
import { MyContext } from "../Example"; // contextのimport
const GrandChild = () => {
const [value] = useContext(MyContext); // 分割代入でstateのみ取得
return (
<div style={{ border: "1px solid black" }}>
<h3>孫コンポーネント</h3>
{value}
</div>
);
};
export default GrandChild;