react + redux-thunk + typescriptでよくあるカウンターのサンプル
redux-thunkをreactとtypescriptで使ってみたかったので、よくある簡単なカウンターを作ってみた。
ただ、typescriptに慣れてなかったりそもそもreduxに触ったばっかりって感じで、表示のガワ部分を作ったあとのactionやreducerあたりは全部presentationalなコンポーネントの中に書いている。
いきなりファイルを分けすぎるとかえって把握しづらかったため。
index.tsx
import React from "react" import ReactDOM from "react-dom" import "./index.css" import * as serviceWorker from "./serviceWorker" import { Provider } from "react-redux" import thunk from "redux-thunk" import { createStore, applyMiddleware } from "redux" import Buttons, { reducer } from "./Buttons" let store = createStore(reducer, applyMiddleware(thunk)) ReactDOM.render( <Provider store={store}> <Buttons /> </Provider>, document.getElementById("root") ) serviceWorker.unregister()
Buttons.tsx(よくあるApp.tsxと同じ)
import * as React from "react" import { connect } from "react-redux" import { Dispatch, Reducer, ActionCreator } from "redux" import AddButton from "./AddButton" import CounterPanel from "./CounterPanel" import DecrementButton from "./DecrementButton" import IncrementButton from "./IncrementButton" import AsyncIncrementButton from "./AsyncIncrementButton" import { ThunkAction, ThunkDispatch } from "redux-thunk" interface CounterState { count: number } enum CounterActionType { ADD = "COUNT/ADD", INCREMENT = "COUNT/INCREMENT", DECREMENT = "COUNT/DECREMENT" } interface CounterAction { type: CounterActionType amount?: number } interface ButtonsProps { count?: number add?: (amount: number) => void increment?: () => void decrement?: () => void asyncIncrement?: () => void } const Buttons: React.FC<ButtonsProps> = ({ count = 0, add = () => {}, increment = () => {}, decrement = () => {}, asyncIncrement = () => {} }) => { return ( <div> <CounterPanel count={count} /> <IncrementButton onClick={increment} /> <DecrementButton onClick={decrement} /> <AddButton onClick={() => add(10)} /> <AsyncIncrementButton onClick={asyncIncrement} /> </div> ) } // action const add = (amount: number): CounterAction => ({ amount, type: CounterActionType.ADD }) const increment = (): CounterAction => ({ type: CounterActionType.INCREMENT }) const decrement = (): CounterAction => ({ type: CounterActionType.DECREMENT }) const asyncIncrement: ActionCreator< ThunkAction<void, CounterActionType, null, CounterAction> > = () => { return (dispatch: Dispatch) => { setTimeout(() => { dispatch({ type: CounterActionType.INCREMENT }) }, 3000) } } // reducer export const reducer: Reducer<CounterState, CounterAction> = ( state: CounterState = { count: 0 }, action: CounterAction ): CounterState => { switch (action.type) { case CounterActionType.ADD: return { ...state, count: state.count + (action.amount || 0) } case CounterActionType.INCREMENT: return { ...state, count: state.count + 1 } case CounterActionType.DECREMENT: return { ...state, count: state.count - 1 } default: const _: never = action.type return state } } interface StateProps { count: number } interface DispatchProps { add: (amount: number) => void increment: () => void decrement: () => void } const mapStateToProps = (state: CounterState): StateProps => ({ count: state.count }) const mapDispatchToProps = ( dispatch: ThunkDispatch<any, any, CounterAction> ) => ({ add: (amount: number) => dispatch(add(amount)), increment: () => dispatch(increment()), decrement: () => dispatch(decrement()), asyncIncrement: () => dispatch(asyncIncrement()) }) export default connect( mapStateToProps, mapDispatchToProps )(Buttons)
typescriptの型指定はまだ全然慣れない。
前の会社ではC++とかJavaとか触ってたけど、今の会社でrubyを触り始めて型についてすべて忘れてしまった。
このサンプルを作ってる中で気になったんだけど、reduxってどの程度hooksで置き換えられるんだろう。
100%は無理にしても割と置き換えられるんじゃないかなあって気がしている。
最近は
この本が良かった。
萌え系って苦手だから表紙でちょっと避けてたけど、中見たら萌え要素全く無かったし、内容もかなりわかりやすく書かれていたしですごくよかった。
そろそろサンプル以上のものを作るようにしたい。