Pascal
Pascal

Reputation: 23

Redux observable doesn't handle backend response

I'm implementing an epic for a backend call. The response doesn't get handled as expected.

For my implementation I'm using these modules:

Action.ts

export const callBackendAction = createStandardAction('data/GET')();
export const callBackendCompletedAction = createStandardAction('data/LOADED')<IResultDataDTO[]>();

Epic.ts

export const callBackendEpic: Epic<RootAction, RootAction> = action$ =>
    action$.pipe(
      filter(isActionOf(callBackendAction)),
      switchMap(async () =>
        from(axiosBackendCall())
          .pipe(
            switchMap(({ data }) => {
              return of(callBackendCompletedAction(data.map(data)))
            }),
            catchError(error => of(console.log(error)))
          )
      ),
      catchError(error => of(console.log(error)))
    );

Reducer.ts

export interface IcallBackendReducerState
{
  data: {}[];
  pending: boolean;
}

const initialState : ICallBackendReducerState =
{
  data: [],
  pending: false
}

export const callBackendReducer = createReducer(initialState)
  .handleAction(callBackendAction, (state: ICallBackendReducerState): ICallBackendReducerState => (
    { ...state, pending: true }
  ))
  .handleAction(callBackendCompletedAction, (state: ICallBackendReducerState, action: PayloadAction<string, any>) => ({
    ...state,
    pending: false,
    data: action.payload
  }));

Redux throws an error which could be related to this problem:

Error: Actions must be plain objects. Use custom middleware for async actions.

My expected output would be a backend call which triggers the reducer to set the pending state to true. <--- this matches the actual output

By receiving the response the reducer should be triggered to update the state with the data and a pending state of false. <--- this doesn't happen

Having a look at the network traffic in my browser shows that the call does complete. The problem is that it's not handled properly.

Upvotes: 1

Views: 181

Answers (1)

martin
martin

Reputation: 96889

It looks like the problem is on these two lines:

switchMap(async () =>
  from(axiosBackendCall())

By using async you make the inner function return a Promise but then you use from() that returns an Observable so in fact this arrow function return Promise<Observable> which is not what you want. switchMap will only subscribe to the Promise and not the nested Observable so redux is confused why instead of an action you're passing Observable.

So the fix is simple, just don't use async because you don't even need it there. from will handle the promise returned from axiosBackendCall() automatically (I'm assuming axiosBackendCall returns a Promise).

switchMap(() =>
  from(axiosBackendCall())
    .pipe(...)
),

Upvotes: 2

Related Questions