0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

createContext(useContext)の内部でstateを扱う簡易的な方法

Last updated at Posted at 2021-11-04

createContext により生成した Contextオブジェクト で扱う変数に状態をもたせたいことはあるかと思います。
テスト的なコードを書いていたのでついでに記事にしてみました。

簡易的な内容なので記事というよりTIPS的な感じでみてもらえればと。

createContextをラップした別ファイルを作っておく

変数NumContext にはグローバルに扱いたい変数の初期値を適当にを設定。
変数NumContextProvider には 戻り値にJSXエレメントの NumContext.Provider を設定することでラップしておきます。

NumContext.jsx
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を呼び出しておく。

index.js
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関数を書く。

App.js
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 を用いています。

NumContextReducer.js
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オブジェクトを分割してから実装するより後から分割した方が問題を切り分けやすいかなと。(先に分割してから実装してしまった)

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?