はじめに
Reactでコンポーネントが多くなると、Propsを使って値の受け渡しが多くなり、ルートのコンポーネントから最下層のコンポーネントへPropsのバケツリレーのようになることがあります。
そこでReact.createContextを使うと、グルーバルなState管理ができるようになり、コードがスッキリし、複雑化するのを防ぐことができるようになります。
React.createContextの使い方をモダンJavaScriptの基本から始める React実践の教科書で勉強したことを参考にまとめていきます。
1.React.createContextでContextの器を作る
はじめにcomponentの下にprovidersというフォルダを作成し、グローバルなState管理をするためのファイルを作成します。ここでは、AdminFlagProvider.jsx
を作成しました。
import { createContext } from "react";
export const AdminFlagContext = createContext({});
2.作成したContextのProviderでグローバルStateを扱いたいコンポーネントを囲う
Providerのコンポーネントは、何でも囲めるようにPropsとしてchildrenを受け取るようにする
のがポイントです。
上記で用意したAdminFlagContextには、Providerが用意されているので、それを返却するようにします。
import { createContext } from "react";
export const AdminFlagContext = createContext({});
export const AdminFlagProvider = (props) => {
// 何でも囲めるようにPropsとしてchildrenを受け取るようにする。
const { children } = props;
const sampleobj = { sampleValue: "テスト" };
return (
// AdminFlagContextには、Providerが用意されているのを返却する。
// valueの中にグローバルに扱う実際の値を設定する
<AdminFlagContext.Provider value={sampleobj}>
{children}
</AdminFlagContext.Provider>
);
};
3.Providerを使いたい範囲のコンポーネントを囲う
今回は、アプリ全体で参照できるようにするために、index.jsの中でAppコンポーネントを囲みます。
import ReactDom from "react-dom";
import { App } from "./App";
import "./index.css";
import { AdminFlagProvider } from "./components/providers/AdminFlagProvider";
ReactDom.render(
<AdminFlagProvider>
<App />
</AdminFlagProvider>,
document.getElementById("root")
);
4.Stateを参照したいコンポーネントでReact.useContextを使う
React.useContextを使い、その引数に対象のContextを指定するだけで参照することができます。下記のコードを実行することで、AdminFlagProvider.jsxで定義したsampleobjの値を習得することができます。
import { useContext } from "react";
import { AdminFlagContext } from "./providers/AdminFlagProvider";
export const EditButton = (props) => {
const AdminFlagValue = useContext(AdminFlagContext);
const { isAdmin } = props;
// useContextで用意したsampleobjの値を取得することができた
console.log(AdminFlagValue);
return <button disabled={!isAdmin}>編集</button>;
};
import { useState } from "react";
import { Card } from "./components/Card";
export const App = () => {
const [isAdmin, setIsAdmin] = useState(false);
const onClickSwitch = () => setIsAdmin(!isAdmin);
return (
<div>
{isAdmin ? <span>管理者です</span> : <span>管理者以外です</span>}
<br />
<button onClick={onClickSwitch}>切り替えボタン</button>
<Card isAdmin={isAdmin} />
</div>
);
};
import { EditButton } from "./EditButton";
export const Card = (props) => {
const { isAdmin } = props;
return (
<div>
{/* propsをそのまま受け渡ししている、バケツリレーを行っている */}
<EditButton isAdmin={isAdmin} />
</div>
);
};
次のステップ!Contextの更新と参照処理を行う
ContextのStateの更新と参照には、Providerを記載しているファイルににStateを定義し、そのStateの値と更新関数をContextのvalueに設定します。
import { createContext, useState } from "react";
export const AdminFlagContext = createContext({});
export const AdminFlagProvider = (props) => {
const { children } = props;
// Contextの更新と参照するためにStateを定義する
const [isAdmin, setIsAdmin] = useState(false);
// ContextオブジェクトとしてisAdminとsetIsAdminを設定(オブジェクトの省略記法)
return (
<AdminFlagContext.Provider value={{ isAdmin, setIsAdmin }}>
{children}
</AdminFlagContext.Provider>
);
};
useContextを使って、グローバルなStateの値を取得することができるようになりました。
さらに、propsで値を受け渡していた箇所を削除することができ、スッキリさせることができます。
import { useContext } from "react";
import { AdminFlagContext } from "./providers/AdminFlagProvider";
// 不要なpropsを削除できた
export const EditButton = () => {
//useContextを使って、グローバルなStateの値を取得する
const { isAdmin } = useContext(AdminFlagContext);
return <button disabled={!isAdmin}>編集</button>;
};
import { EditButton } from "./EditButton";
// 不要なpropsを削除でき、シンプルになりました。
export const Card = () => {
return (
<div>
<EditButton />
</div>
);
};
import { useContext } from "react";
import { Card } from "./components/Card";
import { AdminFlagContext } from "./components/providers/AdminFlagProvider";
export const App = () => {
// useContextから更新するための、Stateを取得を行う。
const { isAdmin, setIsAdmin } = useContext(AdminFlagContext);
//グローバルなStateの値を更新!
const onClickSwitch = () => setIsAdmin(!isAdmin);
return (
<div>
{isAdmin ? <span>管理者です</span> : <span>管理者以外です</span>}
<br />
<button onClick={onClickSwitch}>切り替えボタン</button>
<Card isAdmin={isAdmin} />
</div>
);
};