0
votes

I have been trying to use rxjs properly with Redux observable and Redux actions. I've tried many different combinations of calling the below functions and every possible combination has given me different errors.

Basically, I have two actions that can be dispatched in my app: SET_CIRCULAR_DATA and SET_MODAL_OVER_DEEP_LINK_FLAG.

I'd like another action to be dispatched, determineFoundInProducts() when SET_MODAL_OVER_DEEP_LINK_FLAG happens followed by the first (successful) SET_CIRCULAR_DATA action. Any subsequent SET_CIRCULAR_DATA actions should be ignored until SET_MODAL_OVER_DEEP_LINK_FLAG happens again. Because of this need, I felt withLatestFrom() made the most sense. I also thought that takeUntil() and repeat() might be helpful to help me accomplish what I need, though I'm not including them in this code example because I'm stuck at the withLatestFrom() part.

I've tried different combinations of piping, not piping, using switchMap, map, mapTo, but nothing seems to work. I am importing all the used rxjs functions and I'm combining epics at the root, so it couldn't be that. I just can't seem to get the right combination to not get this to error.

Some examples of errors are

  • Actions must be plain objects. Use custom middleware for async actions.
  • You provided 'undefined' where a stream was expected

I am using RXJS ^5.5.6 and Redux Observable ^0.17.0.

import 'rxjs';
import { Observable } from 'rxjs/Observable';
import { withLatestFrom, map, tap, mergeMap } from 'rxjs/operators';
import ....all the types and actions

export const handleProductDetailsModalOnLoginRedirect = (action) =>
  action.ofType(weeklyAdTypes.types.SET_CIRCULAR_DATA).pipe(
    withLatestFrom(
      action.ofType(navigationTypes.types.SET_MODAL_OVER_DEEP_LINK_FLAG)
    ),
    mergeMap(([first, second]) => {
      console.log(first, second);
      return determineFoundInProducts();
    })
  );

EDIT

I was struggling in my original post to determine what part of the code was breaking. What I had trouble with was that the redux observable action could not communicate with the rxjs functions. What I found was that piping appropriately piped (pun intended) the action to be workable with rxjs functions. Here is what I did.

action.ofType(navigationTypes.types.INIT_REDIRECT_MODAL_LOGIC)
  .switchMap(() =>
    action.ofType(weeklyAdTypes.types.SET_CIRCULAR_DATA)
    .withLatestFrom(action.ofType(navigationTypes.types.SET_MODAL_OVER_DEEP_LINK_FLAG))
      .take(1)
      .pipe(
        map(([first]) => {
         // Do stuff
         return determineFoundInProducts();
      }))
    )
2

2 Answers

2
votes

The answer depends on what you want to happen if multiple SET_MODAL_OVER_DEEP_LINK_FLAG actions are dispatched before any SET_CIRCULAR_DATA action is, as well as whether they should some how be paired together with some sort of unique ID or not.

It sounds most likely like you're looking for exhaustMap.

The exhaustMap operator is basically the opposite behavior of switchMap. It maps inputs to inner observable and flattens its output, ignore other input values until that observable completes. It exhausts the inner Observable.

In your use case it means that we first listen for SET_MODAL_OVER_DEEP_LINK_FLAG, then we listen for a single SET_CIRCULAR_DATA, but while we're waiting for that subsequent SET_CIRCULAR_DATA we ignore all possibly incoming SET_MODAL_OVER_DEEP_LINK_FLAG actions. Note that the take(1) is important, because otherwise we'll listen for a stream of every SET_CIRCULAR_DATA, not just one.

export const handleProductDetailsModalOnLoginRedirect = (action) =>
  action.ofType(navigationTypes.types.SET_MODAL_OVER_DEEP_LINK_FLAG)
    .exhaustMap(() => 
      action.ofType(weeklyAdTypes.types.SET_CIRCULAR_DATA)
        .take(1)
        .map(() => determineFoundInProducts())
    );

Btw it's common to ask Redux Observable questions that are actually just RxJS questions. This is a cool thing to note because the RxJS community is massive so you'll find a TON more resources on it, as well as have a better chance of getting help if you're able to rephrase questions to be agnostic of Redux Observable--e.g. change ofType('TYPE') to a filter(d => d.type === 'TYPE')

0
votes

The above answer will definitely help others. However, what I was struggling with was having redux observable action communicate with RXJS functions. Please see my edit to my original post for solution.