Reactのグローバルstate管理(Reducer + Context)をTypeScriptで記述する

React

React Hooksの標準APIであるuseReducerとuseContextを用いて、Reduxライクなグローバルstate管理をTypeScriptで記述しましたので、共有します。

ユーザの認証状況(status)・認証トークン(token)の2つのstateをグローバル管理し、useAuthState・useAuthDispatchでstateとdispatchを提供します。

import文は省略致します。

Types.context.tsx

import React from "react";

export type UnauthenticatedType = "Unauthenticated";
export type AuthenticatedType = "Authenticated";
export type AuthStatus =
  | UnauthenticatedType
  | AuthenticatedType;
export type Token = string | null;
export type AuthActionType =
  | { type: "COMPLETE_SIGNUP"; token: Token }
  | { type: "SET_TOKEN"; token: Token };
export type AuthState = {
  status: AuthStatus;
  token: Token;
};
export type AuthDispatch = React.Dispatch<AuthActionType>;

AuthContext.tsx

const authReducer = (prevState: AuthState, action: AuthActionType) => {
  let _status = prevState.status;
  switch (action.type) {
    case "COMPLETE_SIGNUP":
      if (prevState.token) return { ...prevState };
      return {
        ...prevState,
        status: AUTHENTICATED,
        token: action.token,
      };

    case "SET_TOKEN":
      if (prevState.token) return { ...prevState };
      return {
        ...prevState,
        token: action.token,
      };
    default:
      console.warn(`Not found this action.type`);
      return { ...prevState };
  }
};

export const UNAUTHENTICATED: UnauthenticatedType = "Unauthenticated"; // signup処理前.
export const AUTHENTICATED: AuthenticatedType = "Authenticated"; // signup処理後.

const AuthStateContext = createContext<AuthState>({
  status: UNAUTHENTICATED,
  token: null,
});
const AuthDispatchContext = createContext<AuthDispatch>(() => {
  return;
});

export const useAuthState = (): AuthState => {
  const context = useContext(AuthStateContext);
  return context;
};
export const useAuthDispatch = (): AuthDispatch => {
  const context = useContext(AuthDispatchContext);
  return context;
};

type Props = {
  status: AuthStatusType;
  token: string;
};
export const AuthProvider: React.FC<Props> = ({
  children,
  status,
  token,
}) => {
  const initAuthState: AuthState = {
    status: status ? status : UNAUTHENTICATED,
    token: token ? token : null,
  };
  const [authState, authDispatch] = useReducer(authReducer, initAuthState);

  return (
    <AuthStateContext.Provider value={authState}>
      <AuthDispatchContext.Provider value={authDispatch}>
        {children}
      </AuthDispatchContext.Provider>
    </AuthStateContext.Provider>
  );
};

コメント