質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
React Native

React Nativeは、ネイティブモバイルアプリ(iOS/Android)を作成できるJavaScriptフレームワークです。Reactと同じ設計のため、宣言的なコンポーネントでリッチなUIを開発することが可能です。

Redux

Reduxは、JavaScriptアプリケーションの状態を管理するためのオープンソースライブラリです。ReactやAngularで一般的にユーザーインターフェイスの構築に利用されます。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

1回答

382閲覧

TypeScriptでRedux.connectのラップ関数を作りたいのです

m0a

総合スコア708

React Native

React Nativeは、ネイティブモバイルアプリ(iOS/Android)を作成できるJavaScriptフレームワークです。Reactと同じ設計のため、宣言的なコンポーネントでリッチなUIを開発することが可能です。

Redux

Reduxは、JavaScriptアプリケーションの状態を管理するためのオープンソースライブラリです。ReactやAngularで一般的にユーザーインターフェイスの構築に利用されます。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2018/01/30 06:07

編集2018/01/31 01:44
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との連携ができるコンポーネントが作れるのが理想なのですが上手く行きません。
どう記述すればいいかご教授下さい

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

自己解決

自己解決しました。
最終的に以下のように書くことができるようになりました

export default Myconnect<Props>( ActionDispatcher, (state, dispatch, ownProps) => ({ ...ownProps, value: state.counter, }))(Counter);

変更点:

import { Counter, Props } from './Counter'; import * as ReactRedux from 'react-redux'; import { Dispatch } from 'redux'; import { decrementAmount, incrementAmount } from './module'; import { ReduxAction, ReduxState } from '../store'; export class ActionDispatcherBase { constructor(protected dispatch: (action: ReduxAction) => void, rootState: ReduxState) { } } export class ActionDispatcher extends ActionDispatcherBase { public increment(amount: number) { this.dispatch(incrementAmount(amount)); } public decrement(amount: number) { this.dispatch(decrementAmount(amount)); } } interface MyMergeProps<T extends {actions: ActionDispatcherBase}> { (state: ReduxState, dispatch: Dispatch<ReduxAction>, ownProps: T): T; } function Myconnect<T extends {actions: ActionDispatcherBase}>( actionDispatcher: typeof ActionDispatcherBase, mergeProps: MyMergeProps<T>): ReactRedux.InferableComponentEnhancer<T> { return ReactRedux.connect( (state: ReduxState) => ({ state }), (dispatch: Dispatch<ReduxAction>) => ({ dispatch }), ({ state }, { dispatch }, ownProps: T) => { const pp = mergeProps(state, dispatch, ownProps); return ( Object.assign({}, pp, { actions: new actionDispatcher(dispatch, state)}) ); }); } export default Myconnect<Props>( ActionDispatcher, (state, dispatch, ownProps) => ({ ...ownProps, value: state.counter, }))(Counter);

投稿2018/01/30 06:54

編集2018/01/31 01:45
m0a

総合スコア708

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問