ゆるふわ技術日誌

エンジニア見習いの悪戦苦闘日記

Day15 #100DaysOfCode

Day15

Redux + TypeScriptでの非同期処理どうするねん問題が解決に近づいた気がする。

ずっとRedux Thunkを用いてなんとかしようとしていたが、Redux Thunkはアクションの代わりにdispatchを引数にとる関数を持つことができるという仕組みで、内部の実装は単にdispatchされてきたActionのTypeがfunctionであるかをみているだけだった。(コードを確認した)

しかし、Reduxのdispatchの型定義をみると以下のようになっている。

export interface Dispatch<A extends Action = AnyAction> {
  <T extends A>(action: T): T;
}

A extends ActionとあるのでActionを継承した何かが渡されることを期待している。

しかし、Redux ThunkのActionはActionを継承した形にはなっていないので、そのままdispatchに渡すと当然エラーとなる。(無理やり型を変えるとかして渡してあげれば動きはすると思うが、それではTypeScriptを使う意味が全くない)

多分TypeScript環境下でRedux Thunkを使うにあまり正攻法はないのではないかという結論に至った。(明らかにそれは違うということがあればぜひ教えて欲しい)

ならばどうするかを考えたときに、昨日読んだ以下のガイドで紹介されていたredux-observableというのを使ってみることにした。

github.com

redux-observableはRxJSをReduxで用いるためのMiddleware(という認識でおそらくあっているはず)

RxJS自体もわかっていなかったので現在進行形で苦しい思いをしているが、Rxなんちゃらはいろんな言語に実装があるので、概念を理解しておいて損はないだろうということでやってみている。

今日やってみてわかったこと。

  • redux-observableはEpicという概念がある
  • Epicには入力されたActionがストリームとして流れて行く
  • 関心のあるActionが入力された際に何らかの処理(おそらく副作用をもたらす処理や外部APIの呼び出しなど)をする

外部APIを呼び出す実装をするとすれば、FETCH_STARTのようなActionを用意して、それが呼び出されたタイミングで外部APIを叩き、結果を別のActionとして返すことで非同期処理を実現できる。

Reduxのdispatchに渡されるActionは純粋なAction(JSのObject)になるので、TypeScript上で扱っても問題になることもない。

問題があるとすれば、Rxの学習コストが高いことかもしれないと思う。公式ドキュメントや解説記事など一通り当たってみたが、まだ感触で3割くらいしか理解できていない気がする。

今日書いてちゃんと動いたEpic

export const asyncIncrementEpic: Epic = action$ => action$.pipe(
  filter(action => action.type === ActionTypes.ASYNC_INC),
  delay(1000),
  mapTo(increment({amount: 1}))
)

1秒後にIncrementするActionが発火するEpic。

filterやdelay、mapToはOperatorというらしい。pipeでOperatorを繋いでいって処理する。

雑談

難しい。しかし、楽しい。

後輩にも負けてられないし、同期にも負けてられない。師匠にも認めてもらいたい。

今が頑張りどきなのだ。