createContext により生成した Contextオブジェクト で扱う変数に状態をもたせたいことはあるかと思います。
テスト的なコードを書いていたのでついでに記事にしてみました。
簡易的な内容なので記事というよりTIPS的な感じでみてもらえればと。
createContextをラップした別ファイルを作っておく
変数NumContext にはグローバルに扱いたい変数の初期値を適当にを設定。
変数NumContextProvider には 戻り値にJSXエレメントの NumContext.Provider を設定することでラップしておきます。
import { createContext, useState } from "react";
export const NumContext = createContext({
number: 0,
handlePlusCountNum: () => null
});
export const NumContextProvider = ({ children }) => {
const [number, setNumber] = useState(0);
const handlePlusCountNum = (number) => setNumber(number + 1);
return (
<NumContext.Provider
value={{
number,
handlePlusCountNum
}}
>
{children}
</NumContext.Provider>
);
};
NumContext の変数で指定している number の値を状態管理したいとします。
この NumContextファイル の中で useState をセットしてあとは handleHoge関数 を用意しておき任意の場所で呼び出して状態管理ができます。
const [number, setNumber] = useState(0);
const handlePlusCountNum = (number) => setNumber(number + 1);
numContextを呼び出してみる
index.jsにはNumContextProviderを呼び出しておく。
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import App from "./App";
import { NumContextProvider } from "./NumContext";
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<NumContextProvider>
<App />
</NumContextProvider>
</StrictMode>,
rootElement
);
App.jsに NumContext を呼び出して Context の状態をカウントアップさせる適当なonClick関数を書く。
import { useContext, useEffect } from "react";
import { NumContext } from "./NumContext";
export default function App() {
const numContext = useContext(NumContext);
return (
<>
<div>{numContext.number}</div>
<button onClick={() => numContext.handlePlusCountNum(numContext.number)}>
Count UP
</button>
</>
);
}
useReducerを使う方法も
扱う状態が多い場合は useReducer を使いたくなると思うので一例を用意しました。
useReducer の state として number を用いています。
import { createContext, useReducer } from "react";
export const NumContextReducer = createContext({
number: 0,
dispatch: () => null
});
const reducer = (state, action) => {
switch (action.type) {
case "increment":
return { number: state.number + 1 };
default:
throw new Error();
}
};
export const NumContextReducerProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, { number: 0 });
return (
<NumContextReducer.Provider
value={{
number: state.number,
dispatch
}}
>
{children}
</NumContextReducer.Provider>
);
};
今回の useState と useReducer を使ったデモページも用意したので置いておきます。
おまけ: 複数の Contextオブジェクト の使用は処理がややこしくなるかもしれない
備考として更新回数を減らす目的で今回の手法を使って2つの Contextオブジェクト を用意してグローバルの状態を扱っていました。
しかし違う Contextオブジェクト 同士をクロスする処理は思わぬところで扱いにくくなる可能性があります。
原因をまだ把握できていないため詳細はひとまず省きますが現在制作しているサイトで複数の Contextオブジェクト を使っておりうまくクロスした際に想定した順序で値が更新されず処理に不具合が生じて扱いにくいコンポーネントになってしまいました。(時間があれば詳細をまとめたい)
回避する方法もあるかと思いますが最初からContextオブジェクトを分割してから実装するより後から分割した方が問題を切り分けやすいかなと。(先に分割してから実装してしまった)