score:2

Accepted answer

There may be some fundamental misunderstandings about RxJS, so I would spend some time to really get a solid foundation on it or consider using something like redux-thunk instead if your async needs aren't more complex than this.

So here are some things to guide you:

  • Your map is returning an Observable, which means you now have an Observable of Observables aka Observable<Observable> which is almost certainly not what you want.
  • It's not clear what Observable.of(socket.emit('verify', { token: 'suc' }))) is meant to do because socket.emit() returns the socket itself, so you're emitting then mapping the action to an Observable of the Socket itself?
  • The mapTo usages are also probably not what you intend. The second one cancels the first, and again you're creating an Observable of Observable. So your epic is emitting (and hence dispatching) a stream of Observables, not actions, which is why you get the "actions must be plain objects" error from redux.

I'm hesitant to give you a solution, but I'll ask that you try to truly understand it rather than just copy-pasting. Maybe take a step back, try to forget all your current beliefs on how Rx works and start fresh? You then might have that "ah ha!" moment :)

I'm guessing you meant to do something like this:

export default function tokenVerifyRequestEpic(action$) {
  return action$.ofType(TOKEN_VERIFY_REQUEST)
    .do(() => {
      socket.emit('verify', { token: 'suc' }));
    })
    .mergeMap(() =>
      Observable.race(
        Observable.fromEvent(socket, 'verifySuccess')
          .mapTo({ type: TOKEN_VERIFY_SUCCESS }),
        Observable.fromEvent(socket, 'verifyError')
          .mapTo({ type: TOKEN_VERIFY_FAILURE })
      )
    );
}

Here it is with verbose inline comments:

export default function tokenVerifyRequestEpic(action$) {
  // filter out all actions except TOKEN_VERIFY_REQUEST
  return action$.ofType(TOKEN_VERIFY_REQUEST)
    // perform the side effect of emitting the 'verify' message
    .do(() => {
      socket.emit('verify', { token: 'suc' }));
    })
    // One problem with this code is that it might not properly account for
    // multiple concurrent TOKEN_VERIFY_REQUEST requests. e.g. How are those
    // handled by the server? How should be they be handled in the UI?
    // If it's not supposed to be possible, it might be useful to assert
    // against that condition so that if it does accidentally happen you
    // throw an error
    .mergeMap(() =>
      // Race between either a 'verifySuccess' or 'verifyError'
      // This assumes one or the other will always happen, if not
      // then you might want to add a timeout() or similar
      Observable.race(
        Observable.fromEvent(socket, 'verifySuccess')
          .mapTo({ type: TOKEN_VERIFY_SUCCESS }),
        Observable.fromEvent(socket, 'verifyError')
          .mapTo({ type: TOKEN_VERIFY_FAILURE })
      )
    );
}

Related Query

More Query from same tag