久違的 Redux Observable。
前置作業
參考範例:stackblitz
定義好 Epic 內要做的事情
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { getPosts, getPostsSuccess, getPostsFailed } from '../features/demoSlice' import { switchMap, catchError, of, from, map } from 'rxjs' import { ofType } from 'redux-observable' import { API } from '../api'
export const getPostsEpic = (action$) => action$.pipe( ofType(getPosts.type), switchMap((action) => from(API.getPosts()).pipe( map((response) => getPostsSuccess(response)), catchError((error) => of(getPostsFailed(error.message))) ) ) )
|
合併成單一支 RootEpic
1 2 3 4 5 6 7 8 9 10 11 12
| import { combineEpics } from 'redux-observable' import * as demoEpics from './demoEpics'
const combineEpicFunctions = (epics) => { return epics.reduce((arr, epic) => { return arr.concat(Object.keys(epic).map((key) => epic[key])) }, []) }
const epics = combineEpicFunctions([demoEpics])
export const rootEpics = combineEpics(...epics)
|
建立 middleware 掛到 store 身上,並且讓 Epic 跑起來
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import { configureStore } from '@reduxjs/toolkit' import demoReducer from '../features/demoSlice' import { rootEpics } from '../epics' import { createEpicMiddleware } from 'redux-observable'
const epicMiddleware = createEpicMiddleware()
export const store = configureStore({ reducer: { demo: demoReducer }, middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(epicMiddleware) })
epicMiddleware.run(rootEpics)
|
同時打多個 API:forkJoin
這個可以想成是 Promise.all
,平常妳是這樣寫:
1 2 3 4
| Promise.all([API.getPosts(), API.getTodos()]).then(([posts, todos]) => { console.log('posts', posts) console.log('todos', todos) })
|
在 Rx 裡面用 forkJoin
是這樣寫:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| export const getPostsEpic = (action$) => action$.pipe( ofType(getData.type), switchMap((action) => forkJoin([from(API.getPosts()), from(API.getTodos())]).pipe( map(([posts, todos]) => getDataSuccess({ posts, todos }) ), catchError((error) => of(getDataFailed(error.message))) ) ) )
|
Promise.all
是接收用 Array 組成的 Promise
,而 forkJoin
則是接收用 Array 組成的 Observable,然後兩個都會拿到 [result1, result2, ...]
的結果。
先打 A 在打 B 的 API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| export const getPostsEpic = (action$) => action$.pipe( ofType(getData.type), switchMap((action) => { return from(API.getPosts()).pipe( mergeMap((postsReponse) => { console.log('A succeeded', postsReponse) return from(API.getTodos()).pipe( map((todosReponse) => { console.log('B succeeded', todosReponse) return getDataSuccess({ posts: postsReponse, todos: todosReponse }) }) ) }), catchError((error) => of(getDataFailed(error.message))) ) }) )
|