1
votes

I have an ngrx meta-reducer which adjusts the payload of the action like so:

export function doSomething(reducer) {
  return (state, action) => {
    action.payload = 'fred'
    return reducer(state, action)
  }
}

export const metaReducers: MetaReducer<State>[] = [doSomething]

This works fine. However, I want the payload to be determined by the response of an asynchronous HTTP request. Something like this:

export function doSomething(reducer) {
  return (state, action) => {
    // ASSUME THE HTTP SERVICE IS AVAILABLE AS MAKING THE HTTP SERVICE AVAILABLE IS NOT A PROBLEM (trying to keep the code simple for illustration)
    this.http.get('/some/data').subscribe((res) => {
      action.payload = res.data
      return reducer(state, action)
    })
  }
}

But this does not work. Anyone know how to achieve this? thx

1
Isn't this what effects are for? - R. Richards
yeah but i want something generic that all actions will trigger without having to write an effect for every single one - or maybe your question is helpful - if I dont use the ofType filter in an effect it might just work - danday74
I would be curious to know if that works for you. - R. Richards
I have raised a feature request here regarding this issue github.com/ngrx/platform/issues/1649 - it has been closed rather abruptly but have requested it be reopened - danday74
Yes I want this too. Ie use Ionic storage for local store to SQLite, which is async (when running on device with SQLite available) - peterc

1 Answers

0
votes

Not happy with this solution but it is a workaround. If anyone knows how to write an async meta reducer I'd be much happier :)

This code, for any action triggered whose type ends with "Requested" makes an HTTP request and then fires a "Loaded" action to populate the store with the HTTP response.

For example, if I dispatch:

Action "[Anything] Potatoes Requested"

It will make the HTTP request:

HTTP GET /anything-potatoes-requested

And will then get the response and dispatch:

Action "[Anything] Potatoes Loaded" to populate the store with the response.

This means backend data goes straight into the store and I don't need to write an @Effect for every "Requested" action.

This would be much simpler though if async meta reducers were possible, since then I would not need a "Loaded" action but could modify the payload of the original "Requested" action before it hit the reducers.

Here's the main snippet of code:

@Effect()
requested$ = this.actions$.pipe(
  filter(action => action.type.endsWith('Requested')),
  map(action => ({
    action,
    path: getActionHttpPath(action),
    loadedAction: getLoadedAction(action)
  })),
  filter(obj => obj.loadedAction != null),
  tap(obj => debug('HTTP GET', obj.path)),
  mergeMap((obj) =>
    this.http.get(obj.path).pipe(
      map(res => ({...obj, res})),
      catchError(() => {
        return of({...obj, res: null})
      })
    )
  ),
  filter(obj => obj.res != null),
  map(obj => new obj.loadedAction(obj.res))
)