export default connect( (state: ReduxState) => ({ state }), (dispatch: Dispatch<ReduxAction>) => ({ dispatch }), ({state}, {dispatch}, onwProps) => ({ actions: new ActionDispatcher(dispatch, state), value: state.counter, }) )(Counter);
上記conncetを毎回書くのが面倒なので上手くラップする関数が作りたいんです。
まず、非同期処理を行うためにActionDispacherを作りました。非同期処理を行えるクラスです。
参考資料:React + Redux + TypeScriptの最小構成
counterコンポーネントを以下の構成で作っています
- 今回見ていただきたいActionDispacher
src/counter/Container.tsx
ts:src/counter/Container.tsx
1import { Counter } from './Counter'; 2import { connect } from 'react-redux'; 3import { Dispatch } from 'redux'; 4import { decrementAmount, incrementAmount } from './module'; 5import { ReduxAction, ReduxState } from '../store'; 6 7export class ActionDispatcher { 8 constructor(private dispatch: (action: ReduxAction) => void, rootState: ReduxState) { } 9 10 public increment(amount: number) { 11 this.dispatch(incrementAmount(amount)); 12 } 13 14 public decrement(amount: number) { 15 this.dispatch(decrementAmount(amount)); 16 } 17} 18 19/* 少し複雑 */ 20export default connect( 21 (state: ReduxState) => ({ state }), 22 (dispatch: Dispatch<ReduxAction>) => ({ dispatch }), 23 ({state}, {dispatch}, onwProps) => ({ 24 actions: new ActionDispatcher(dispatch, state), 25 value: state.counter, 26 }) 27)(Counter); 28
- UI
src/counter/Counter.tsx
ts:src/counter/Counter.tsx
1 2import * as React from 'react'; 3import { CounterState } from './module'; 4import { ActionDispatcher } from './Container'; 5 6interface Props { 7 value: CounterState; 8 actions: ActionDispatcher; 9} 10 11export class Counter extends React.Component<Props> { 12 13 render() { 14 return ( 15 <div> 16 <p>score: {this.props.value.num}</p> 17 <button onClick={() => this.props.actions.increment(3)}>Increment 3</button> 18 <button onClick={() => this.props.actions.decrement(2)}>Decrement 2</button> 19 </div> 20 ); 21 } 22}
- reducer
counter/module.tsx
ts:counter/module.tsx
1// ActionCreator 2import { Action } from 'redux'; 3 4enum ActionNames { 5 INC = 'counter/increment', 6 DEC = 'counter/decrement', 7} 8 9interface IncrementAction extends Action { 10 type: ActionNames.INC; 11 plusAmount: number; 12} 13export const incrementAmount = (amount: number): IncrementAction => ({ 14 type: ActionNames.INC, 15 plusAmount: amount 16}); 17 18interface DecrementAction extends Action { 19 type: ActionNames.DEC; 20 minusAmount: number; 21} 22 23export const decrementAmount = (amount: number): DecrementAction => ({ 24 type: ActionNames.DEC, 25 minusAmount: amount 26}); 27 28// reducer 29export interface CounterState { 30 num: number; 31} 32 33export type CounterActions = IncrementAction | DecrementAction; 34 35const initialState: CounterState = { num: 0 }; 36 37export default function reducer(state: CounterState = initialState, action: CounterActions): CounterState { 38 switch (action.type) { 39 case ActionNames.INC: 40 return { num: state.num + action.plusAmount }; 41 case ActionNames.DEC: 42 return { num: state.num - action.minusAmount }; 43 default: 44 return state; 45 } 46}
上記は動く構成です。
ここで注目していただきたいのはsrc/counter/Container.tsxです。
export default connect( (state: ReduxState) => ({ state }), (dispatch: Dispatch<ReduxAction>) => ({ dispatch }), ({state}, {dispatch}, onwProps) => ({ actions: new ActionDispatcher(dispatch, state), value: state.counter, }) )(Counter);
connectの呼び出しが複雑なので汎用のラップ関数を作ろうと思いました。 しかし上手く行きません。
とりあえず書いてみました。
ts
1import { Counter } from './Counter'; 2import * as ReactRedux from 'react-redux'; 3import { Dispatch } from 'redux'; 4import { decrementAmount, incrementAmount } from './module'; 5import { ReduxAction, ReduxState } from '../store'; 6 7export class ActionDispatcherBase { 8 constructor(protected dispatch: (action: ReduxAction) => void, rootState: ReduxState) { } 9} 10 11export class ActionDispatcher extends ActionDispatcherBase { 12 13 public increment(amount: number) { 14 this.dispatch(incrementAmount(amount)); 15 } 16 17 public decrement(amount: number) { 18 this.dispatch(decrementAmount(amount)); 19 } 20} 21 22interface MyMergeProps<T> { 23 (state: ReduxState, dispatch: Dispatch<ReduxAction>, onwProps: T): T; 24} 25 26function Myconnect<T>(actionDispatcher: typeof ActionDispatcherBase, mergeProps: MyMergeProps<T>) { 27 return ReactRedux.connect( 28 (state: ReduxState) => ({ state }), 29 (dispatch: Dispatch<ReduxAction>) => ({ dispatch }), 30 ({ state }, { dispatch }, onwProps: T) => { 31 const pp = mergeProps(state, dispatch, onwProps); 32 return ( 33 Object.assign({}, { 34 actions: new actionDispatcher(dispatch, state) 35 }, pp) 36 ); 37 }); 38} 39 40// export default ReactRedux.connect( 41// (state: ReduxState) => ({ state }), 42// (dispatch: Dispatch<ReduxAction>) => ({ dispatch }), 43// ({ state }, { dispatch }, onwProps) => ({ 44// actions: new ActionDispatcher(dispatch, state), 45// value: state.counter, 46// }) 47// )(Counter); 48export default Myconnect( 49 ActionDispatcher, (state, dispatch, ownprops) => ({ 50 value: state.counter, 51}))(Counter);
最終的に
export default Myconnect( ActionDispatcher, (state, dispatch, ownprops) => ({ value: state.counter, }))(Counter);
のような呼び出してRedux Stateとの連携ができるコンポーネントが作れるのが理想なのですが上手く行きません。
どう記述すればいいかご教授下さい
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。