React×TypescriptでuseContext使ってReduxライクなState管理をしてみる

この記事は約 4 分で読めます。

みなさんこんにちは!ひろぽんです!

ブログ継続3日目!おめでとう俺!!!!

金曜日からまた再開するで!って言って3日目!意味の分からんブログを書きつつもついに3日連続更新できました!

この勢いで更新していきます!

さて!今回はReact×TypescriptでuseContextを使ってreduxライクなState管理をしてみる!ということなんですが、単にuseContextを使ってdispatchをvalueに持たせたりstateを持たせたりしていこう!って話です!

では早速

counterReducerを作る

const IncrementAction = 'inc' as const;
export const Increment = () => ({type : IncrementAction});

const DecrementAction = 'dec' as const;
export const Decrement = () => ({type : DecrementAction});

export type CounterAction = ReturnType<typeof Increment> | ReturnType<typeof Decrement>

type CounterState = {
    count : number
}

export const CounterInitialState : CounterState = {
    count : 0
}

export const CounterReducer : React.Reducer<CounterState,CounterAction> = (state = CounterInitialState,actions) =>
{
    switch (actions.type) {
        case IncrementAction:
            return {count : state.count + 1}
        case DecrementAction:
            return {count : state.count - 1}
        default:
            return state
    }
};

さてここまで一呼吸でreducerを作りました!

ここでやってるのはシンプルで、ActionTypeをConstで宣言してActionCreaterを作ってそれらの戻り値の型をreducerにぶちこんでるだけですね!

createContextでContextとProviderを作る

import React,{createContext, FC, useReducer} from "react";
import {CounterActions, CounterInitialState, CounterReducer} from "./CounterReducer";

type CounterContextState = {
    count : number,
    dispatch : React.Dispatch<CounterActions>
}

export const CounterContext = createContext<CounterContextState>({} as CounterContextState);

export const CounterContextProvider : FC = ({children}) => {
    const [state,dispatch] = useReducer<typeof CounterReducer>(CounterReducer,CounterInitialState);
    return(
        <CounterContext.Provider value={{
            count : state.count,
            dispatch : dispatch
        }
        }>
            {children}
        </CounterContext.Provider>
    )
};

ここではContextProviderに持たせたいvalueの中の型を指定していき、returnするところで実際にvalueにあたいとかdispatchを入れていかないといけません。

その型宣言とContextを作っている箇所が下記です。

type CounterContextState = {
    count : number,
    dispatch : React.Dispatch<CounterActions>
}

export const CounterContext = createContext<CounterContextState>({} as CounterContextState);

で、上記のCounterContextのProviderを作ってるのが下記。

export const CounterContextProvider : FC = ({children}) => {
    const [state,dispatch] = useReducer<typeof CounterReducer>(CounterReducer,CounterInitialState);
    return(
        <CounterContext.Provider value={{
            count : state.count,
            dispatch : dispatch
        }
        }>
            {children}
        </CounterContext.Provider>
    )
};

ここではuseReducerでreducerを使う下準備をして、その戻り値にstateとdispatchを設定してそれぞれvalueにぶち込んでます。

実際useContextで上記で作ったものを使ってみる。

app.tsxを下記のように書き換えます。

import React, {FC, useContext} from 'react';
import logo from './logo.svg';
import './App.css';
import {CounterContext, CounterContextProvider} from "./Contexts/CounterProvider";
import {Decrement, Increment, IncrementAction} from "./Reducers/CounterReducer";

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

const Counter : FC = () => {
  const context = useContext(CounterContext)
  return(
      <div>
        <h2>this is counter app.</h2>
        <p>{context.count}</p>
        <button onClick={() => context.dispatch(Increment())}>increment</button>
        <button onClick={() => context.dispatch(Decrement())}>decrement</button>
      </div>

  )
};

export default App;

CounterContextProviderのタグでくくった中にContextを使いたいコンポーネントを入れます。

あとはそのコンポーネントの中でuseContextをすればよいだけですね!

結果はこんな感じです!

これでReduxライクなstate管理ができたのではないでしょうか!

今回のコードはGitHubで公開しています!

下記リポジトリの中に今回のブログで書いてきたコードを「001_hooks_context」というフォルダで格納しているので、自由にお使いください!

https://github.com/adaman3568/react-type-demo

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA