TypeScript+useContext+useReducerで簡易な状態管理を実装する

4 min read読了の目安(約2600字

概要

  • TypeScript環境でuseContext+useReducerを用いて簡易な状態管理をするための最低限の書き方
  • 記述量が割と多く、毎回思い出しながら書いていているとモタつくので、自分のために記録
  • あまり熟練者ではないので、おかしな箇所やもっとこうした方が良いなどあれば、教えてくださいm(_ _)m

内容

  • 新規メニューを追加するまでのユーザーの入力値を保持するステート
    • (何のメニューだよ、って感じですが、特に「メニュー」という状態の特性は意識しなくて大丈夫です)
  • 状態管理のための最低限の内容だけ記載してます

store/newMenu/index.ts

  • createContextで状態管理用の枠を作成
  • useReducerでstateと変更用関数をカスタムフックで定義
import React from 'react';
import { NewMenuActionType, NewMenuState, NewMenuStateWithAction } from './newMenuType';
import reducer from './reducer';

type UseNewMenuStateType = () => {
  newMenuState: NewMenuState
  dispatchNewMenuState: React.Dispatch<NewMenuActionType>
}

const initialState: NewMenuState = {
  title: '',
  // その他もろもろ
};

export const NewMenuContext = React.createContext<NewMenuStateWithAction>(
  { newMenuState: initialState, dispatchNewMenuState: () => {} },
);

export const useNewMenuState: UseNewMenuStateType = () => {
  const [newMenuState, dispatchNewMenuState] = React.useReducer(reducer, initialState);
  return { newMenuState, dispatchNewMenuState };
};

store/newMenu/reducer.ts

  • 変更用関数の中身を定義
import { Reducer } from 'react';
import { NewMenuState, NewMenuActionType } from '@store/newMenu/newMenuType';

const reducer: Reducer<NewMenuState, NewMenuActionType> = (
  oldState, action,
) => {
  switch (action.type) {
    case 'changeTitle': return { ...oldState, title: '変更後のタイトル' };
    default: return oldState;
  }
};

export default reducer;

store/newMenu/newMenuType.ts

  • ここで型を定義
import { Dispatch } from 'react';

export interface NewMenuState {
  title: string
}

export type NewMenuActionType =
  | { type: 'changeTitle' }

export type NewMenuStateWithAction = {
  newMenuState : NewMenuState
  dispatchNewMenuState: Dispatch<NewMenuActionType>
}

components/NewMenu.tsx

  • メニューstateをもつ最上部の親を設定
  • ここまでの4ファイルで事前準備完了
import React from 'react';
import { NewMenuContext ,useNewMenuState } from '@store/newMenu';

export default () => {
  const newMenuState = useNewMenuState();

  return (
    <NewMenuContext.Provider value={newMenuState}>
      <SomeComponent />
    </NewMenuContext.Provider>
  );
};

conponents/SomeConponent.tsx

  • 実際に値を使ったり変更したりの例
import React from 'react';
import { NewMenuContext } from '@store/newMenu';

export default () => {
  const { newMenuState, dispatchNewMenuState } = React.useContext(NewMenuContext);

  return (
    <>
      <p>{newMenuState.title}</p>
      <button
        onClick={() => dispatchNewMenuState({ type: 'changeTitle' })}
      />
    </>
  )
}

以上です。関係ないですが、Recoilに期待しています。