10

In redux-observable, epics are accepting stream of actions and returning back new stream of actions. In my use-case I need to send analytics event after some action was dispatched and do nothing after.

With redux-saga, I can just listen that action using takeEvery, and perform a side-effect inside of a saga function:

function* saga() {
  yield takeEvery('SOME_ACTION', function*() {
    sendAnalytics();
  })
}

But how can I achieve the same with redux-observable? There is pretty much side effects, that are not requiring dispatching new actions, like initializing plugins, logging, setting cookies etc...

If it's an anti-pattern for both of these libraries, what solution should be used for these kinds of effects?

jayphelps
  • 15,276
  • 3
  • 41
  • 54
1ven
  • 6,776
  • 1
  • 25
  • 39
  • I haven't used either `redux-saga` or `redux-observable`, but I've done this particular side effect of tracking by just calling the analytics module inside of `mapDispatchToProps`, which is a legitimate place to do this in my opinion. So before or after you call `dispatch` you just do something like `googleAnalytics.trackEvent(...)`. – timotgl Aug 09 '17 at 11:18
  • Thanks, but in my opinion, `mapDispatchToProps` functions is not the right place to perform side-effects – 1ven Aug 10 '17 at 06:53

1 Answers1

22

Definitely not an anti-pattern outright to have an epic which is "read-only"--but I want to caution that it's often a sign a person is doing something less-than-idiomatic, but this is not one of those cases.

There are likely many ways of accomplishing this in Rx. Here are two:

do + ignoreElements

I think this is the most clear of what it does. do is only ever used to perform a side effect (often logging) for next/error/complete without affecting the stream. We then use ignoreElements to prevent the SOME_ACTION from being dispatched again (which would cause infinite recursion). See the rxjs docs for more info on these operators

const someEpic = action$ =>
  action$.ofType('SOME_ACTION')
    .do(() => sendAnalytics())
    .ignoreElements();

Anonymous Observable

This is a somewhat more "lightweight" solution since it doesn't use any operators other than ofType. I would caution against using this one though until you and your teammates have a solid grasp of RxJS and particularly creating custom/anonymous Observables. i.e. don't write code you don't understand.

const someEpic = action$ =>
  new Observable(() =>
    action$.ofType('SOME_ACTION')
      .subscribe(() => sendAnalytics()) // subscription is returned so it can be cleaned up
  );

This use case would be a great addition to the Recipes section of the docs, if someone ever has the time to PR it.


Btw, this is mostly an RxJS question where the stream just happens to be actions. You might find you get answers and better support long-term if you search or phrase your questions in the context of RxJS instead of redux-observable. Nearly all redux-observable questions are actually RxJS questions, which is great cause the knowledge is transferable!

jayphelps
  • 15,276
  • 3
  • 41
  • 54
  • 3
    I'd just like to say, I really appreciate the thought and effort you put into clearly and completely answering redux-observable questions, @jayphelps! I not only learn redux-observable, but also valuable RxJS tips. So, thanks! – IanVS Nov 16 '17 at 04:31
  • Why do you use ignoreElements instead of NEVER? Which one is better? – Fen1kz Oct 10 '18 at 13:35
  • 1
    @Fen1kz `never()` is a static Observable _factory_--it returns a source Observable that never emits, never completes, never errors. `ignoreElements()` on the other hand is an _operator_--meaning it operates on another source observable rather than being a static producer itself--that when applied to an Observable will simply ignore any values that source Observable emits. So not the same :) `ignoreElements()` will NOT ignore errors/complete. – jayphelps Oct 22 '18 at 03:59