React Context を用いた簡易 Store

reactfluxby mizchi created at 2020/05/27/16:09

ToC

課題

  • redux を引っ張り出すと大仰になる。Context 下に共有ステートを持ってそこに setState できるだけでよい。
  • なので、次の 2 つを用意する
    • 現在の state を参照する const appState = useAppState()
    • 現在の state を更新する関数を返す const setAppState = useSetAppState()
  • React.useState() と違って分割している理由は、主にパフォーマンス上の理由
    • 大域な参照なので、可能な限りステートを参照したくない
    • setState() の API は (prevState: State) => State も取れるので、状態更新用途に限ってはそもそも useAppState() せずに済むことが多い
  • でも毎回書いてるけどボイラープレート感強い上に忘れるのでここにメモする

毎回書いてるボイラープレート

// src/contexts/AppStateContext.tsx
import React, { Dispatch, SetStateAction, useContext, useState } from "react";

export type AppState = {
  value: number;
};

const initialState: AppState = {
  value: 0,
};

const AppStateContext = React.createContext<AppState>(initialState);
const SetAppStateContext = React.createContext<
  Dispatch<SetStateAction<AppState>>
>(() => {});

export function useAppState() {
  return useContext(AppStateContext);
}
export function useSetAppState() {
  return useContext(SetAppStateContext);
}

export function AppStateProvider(props: {
  initialState?: AppState;
  children: React.ReactNode;
}) {
  const [state, setState] = useState<AppState>(
    props.initialState ?? initialState
  );
  return (
    <AppStateContext.Provider value={state}>
      <SetAppStateContext.Provider value={setState}>
        {props.children}
      </SetAppStateContext.Provider>
    </AppStateContext.Provider>
  );
}

使い方

import React from "react";
import ReactDOM from "react-dom";
import {
  AppStateProvider,
  useAppState,
  useSetAppState,
} from "./contexts/AppStateContext";

function Counter() {
  const state = useAppState();
  const setAppState = useSetAppState();
  return (
    <div>
      {state.value}
      <button
        onClick={() => {
          setAppState((s) => ({ value: s.value + 1 }));
        }}
      >
        ++
      </button>
    </div>
  );
}

function App() {
  return (
    <AppStateProvider>
      <Counter />
    </AppStateProvider>
  );
}

ReactDOM.render(<App />, document.querySelector("#root"));
History
6818a86 - Update Sat Jun 6 19:41:40 2020 +0900 
fc5b53c - Update Wed May 27 16:19:07 2020 +0900