javascriptreduxreactive-programmingredux-sagaredux-observable

Why use Redux-Observable over Redux-Saga?


I have used Redux-Saga. Code written with it is easy to reason so far, except JS generator function is messing up my head from time to time. From my understanding, Redux-Observable can achieve the similar job that handles side effects but without using generator function.

However, docs from Redux-Observable does not provide many opinions of why it is superior against Redux-Saga. I would want to know whether not using generator function is the only benefit of using Redux-Observable. And what could be the downsides, gotcha or compromises from using Redux-Observable instead of Redux-Saga?


Solution

  • Disclaimer: I am one of the authors of redux-observable so it's hard for me to be 100% impartial.

    We don't currently provide any reason redux-observable is better than redux-saga because...it's not. 😆

    tl;dr there are pros and cons to both. Many will find one more intuitive than the other, but both are complex to learn in different ways if you don't know RxJS (redux-observable) or generators/"effects as data" (redux-saga).

    They solve the same problem in extremely similar ways, but have some fundamental differences that only become truly apparent once you use them enough.

    redux-observable defers nearly everything to idiomatic RxJS. So if you have RxJS knowledge (or gain it), learning and using redux-observable is super super natural. That also means that this knowledge is transferable to things other than redux. If you decide to switch to MobX, if you decide to switch to Angular2, if you decide to switch to some future hotness X, chances are extremely good that RxJS can help you. This is because RxJS is a generic async library, and in many ways is like a programming language in itself—the whole "Reactive Programming" paradigm. RxJS existed since 2012 and started as a port of Rx.NET (there are "ports" in nearly every major language, it's that useful).

    redux-saga provides its time-based operators itself, so while the knowledge you acquire about generators and handling side effects in this process-manager style is transferable, the actual operators and usage is not used in any other major library. So that's a little unfortunate, but certainly shouldn't be a deal-breaker by itself.

    It also uses "effects as data" (described here), which might be hard to wrap your head around at first, but it means that your redux-saga code doesn't actually perform the side effects itself. Instead, the helper functions you use create objects that are like tasks that represent the intent to do the side effect and then the internal library performs it for you. This makes testing extremely easy, without the need for mocking and is very attractive to some people. However, I personally have found it means your unit tests reimplement much of your saga's logic--making those tests not very useful IMO (this opinion isn't shared by everyone)

    People often ask why we don't do something like that with redux-observable: to me it's fundamentally incompatible with normal idiomatic Rx. In Rx, we use operators like .debounceTime() that encapsulates the logic required to debounce, but that means if we wanted to make a version of it that doesn't actually perform the debouncing and instead emits task objects with the intent, you've now lost the power of Rx because you can't just chain operators anymore because they'd be operating on that task object, not the real result of the operation. This is really hard to explain elegantly. It again requires heavy understanding of Rx to understand the incompatibility of approaches. If you really want something like that, check out redux-cycles which uses cycle.js and mostly has those goals. I find it requires too much ceremony for my tastes, but I encourage you to give it a spin if it interests you.

    As ThorbenA mentioned, I don't shy away from admitting that redux-saga is currently (10/13/16) the clear leader in complex side effect management for redux. It was started earlier and has a more robust community. So there's a lot of attraction to using the de facto standard over the new kid on the block. I think it's safe to say if you use either without prior knowledge, you're in for some confusion. We both use fairly advanced concepts that once you "get", makes complex side effect management far easier, but until then many stumble.

    The most important advice I can give is not to bring in either of these libraries before you need them. If you're only making simple ajax calls, you probably don't need them. redux-thunk is stupid simple to learn and provides enough for the basics--but the more complex the async the harder (or even impossible) it becomes for redux-thunk. But for redux-observable/saga in many ways it shines the most the more complex the async is. There's also a lot of merit in using redux-thunk with one of the others (redux-observable/saga) in the same project! redux-thunk for your common simple stuff and then only using redux-observable/saga for complex stuff. That's a great way to remain productive, so you're not fighting redux-observable/saga for things that would be trivial with redux-thunk.