How to chain actions and epics?

2020-07-18 04:07发布

问题:

I'm familiar with React and Redux but quite new to RxJS and redux-observable.

I'm working on a flow that I've managed to solve with something that looks like this:

const reducer = (state, action)  => {
   switch(action.type) {
   // some other cases here
   case Types.LOCATION_GEOCODED: 
      const { address, place_id } = action
      return {...state, address, place_id }
   }
}

const updateLocationEpic = (action$, store) =>
   action$.ofType(Types.UPDATE_LOCATION)
   .mergeMap((action) =>
      Observable.fromPromise(reverseGeocode(store.getState().location))
      .map(({address, place_id}) => ({type: Types.LOCATION_GEOCODED, address, place_id}))
   )

const broadcastLocationEpic = (action$, store) =>
   action$.ofType(Types.LOCATION_GEOCODED)
   .map((action) => ({type: Types.NEW_CURRENT_LOCATION, store.getState().location}))

The first epic issues a LOCATION_GEOCODED action after the async request, the reducer then "completes" the location information, and finally the second epic takes the complete location from the state and broadcasts it.

The key thing is to ensure the NEW_CURRENT_LOCATION action is trigger after the reducer has processed the previous action (LOCATION_GEOCODED) because it needs to query the state that has just been updated.

The code above works, but I wonder if it can be somehow expressed on a single epic, specially because I have another case on which I need to wait for two actions that are yielded by parallel requests so I would need to do something similar to what you would achieve with a Promise.all kind of pattern

Is there a better way to execute this on a single epic?

回答1:

const updateLocationEpic = (action$, store) =>
    action$.ofType(Types.UPDATE_LOCATION)
        .mergeMap((action) => Observable.merge(
            Observable.fromPromise(reverseGeocode(store.getState().location))
                .map(({ address, place_id }) => ({ type: Types.LOCATION_GEOCODED, address, place_id })),

            action$.ofType(Types.LOCATION_GEOCODED)
                .map((action) => ({ type: Types.NEW_CURRENT_LOCATION, location: store.getState().location }))
        ));