動くコード図鑑技術記事現場の渡り方キャリア論すべての記事About
技術記事

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

バイブス父さん
現役の業務SE
2020年10月25日6 min read
React×TypescriptでuseContext使ってReduxライクなState管理をしてみる

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

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

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

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

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

では早速

counterReducerを作る

counterReducerを作る (typescript)#95f4059e973a
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
    }
};
▸ この snippet は実行結果未収録
▸ 実行結果は未収録です

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

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

createContextでContextとProviderを作る

createContextでContextとProviderを作る (typescript)#299994aedd3b
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>
    )
};
▸ この snippet は実行結果未収録
▸ 実行結果は未収録です

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

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

createContextでContextとProviderを作る (typescript)#bcf866e4af9f
type CounterContextState = {
    count : number,
    dispatch : React.Dispatch<CounterActions>
}
 
export const CounterContext = createContext<CounterContextState>({} as CounterContextState);
▸ この snippet は実行結果未収録
▸ 実行結果は未収録です

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

createContextでContextとProviderを作る (typescript)#c5010fb65476
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>
    )
};
▸ この snippet は実行結果未収録
▸ 実行結果は未収録です

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

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

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

実際useContextで上記で作ったものを使ってみる。 (typescript)#eeb0f49121db
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;
▸ この snippet は実行結果未収録
▸ 実行結果は未収録です

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

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

結果はこんな感じです!

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

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

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

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

この記事のコードと手順は ぜんぶ動作検証済み。 安心して現場で試してくれ。
バイブス父さん

現役の業務SE。C# / SQL Server 保守の現場から、コードも人もキャリアも全部書く。 実体験ベース。

運営者について