みなさんこんにちは!ひろぽんです!
ブログ継続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」というフォルダで格納しているので、自由にお使いください!
コメント