簡單紀錄一下寫 RxJS 時踩到的雷。
新手常常搞錯的觀念:map 跟 mergeMap 的差別
map
是用來做轉換資料:
1 | fromEvent(window, 'click') |
mergeMap
是用來壓平 Observable,代表在 mergeMap
內回傳的東西一定是一個 Observable,而不是一般的資料型態。
先來看錯誤的寫法:
1 | const btn = document.querySelector('.btn') |
會寫出這樣的東西通常是因為你以為這個跟 Array.flat
的用法一樣,就算我傳進去的值不是一個巢狀的 Array,我還是可以拿到結果:
1 | const array = [1, 2, 3] |
但是 mergeMap
的概念不是這樣,它只會預期你回傳一個 Observable(或者是 iterable),所以如果你回傳的東西不對的話就會噴一個 InvalidObservableTypeError
的錯誤。
正確的作法應該是搭配 of
來把值轉換成一個 Observable
:
1 | fromEvent(btn, 'click') |
因為這裡有先用 of
把 e.target
轉換成 Observable 以後再回傳,所以 mergeMap
就會把 Observable 壓平,拿到 Observable 裡面的值(這邊是 button 的 DOM 元素),再丟給 subscribe
,印出 button 元素。
mergeMap
、concatMap
、switchMap
這幾種 Map 都是同樣的原理,只要你用了它們,就永遠要記得回傳一個 Observable 給它。
常見的應用場景:Redux observable
這段只是做個補充,我當初就是在寫 Redux observable 的時候就是因為分不清楚 map
跟 switchMap
的實際差別,所以才會有點看不懂自己到底在寫什麼。
下面是一個簡單的 Epic:
1 | class API { |
這個 Epic 是在接收到對應的 action 時去呼叫 API,成功以後再發出一個新的 action 到 reducer。
不過這樣子寫有一些問題:
switchMap
本身也是一種 map,所以可以直接在裡面決定要回傳什麼 action 就好了- 現在的寫法沒辦法做錯誤處理
比較正規的作法是這樣子:
1 | export const getPostsEpic: Epic = ($action) => |
第一個是我們把要回傳 action 的邏輯都搬移到 from(API.getPosts())
這個 Observable 底下處理,如果 API 沒有問題的話就用 map
回傳 getPostsResult
,否則就在 catchError
內回傳 getPostsFailed
。
第二個是要注意 catchError
的回傳值必須是一個 Observable,所以才需要用 of
來包起來。