
JavaScript Power Tools: Real-World Redux-Saga Patterns - thekenwheeler
http://formidable.com/blog/2017/real-world-redux-saga-patterns/
======
eric_b
I worked on a fairly large and high traffic React app. It had some complicated
async flows, so the team moved from redux-thunk to sagas.

The claims that testability is improved do not ring true to me. Some of those
tests were just awful to write and maintain. I'm sure someone will say "you
were doing it wrong" but there were many folks on the team following advice
from the maintainers themselves on Discord. General understanding of Sagas by
less seasoned developers was also worse than trying to teach them promises.

After the codebase was converted, the team generally concurred that sagas were
a waste of time and effort, especially with async/await gaining steam. They
also felt that maintainability and debug-ability suffered with sagas.
Reasoning about what code runs next is easy, until it's not.

YMMV, but I've tried all the async patterns, and I think sticking with
async/await and promises is probably the best bet these days. The code might
not "look" as pretty - but aesthetics should hardly be the main goal.

~~~
mhink
> After the codebase was converted, the team generally concurred that sagas
> were a waste of time and effort, especially with async/await gaining steam.

To be fair, I'd almost certainly recommend async/await for simpler use cases-
in particular, an application in which API calls are the only async behavior.
However, in our situation, redux-saga turned out to work pretty well. We
needed to coordinate some relatively complex UI flows (the user would draw on
a map, then the app would POST the coords to the server). Before introducing
redux-saga, we were starting to get lost in a daisy-chain of Redux actions.

> Reasoning about what code runs next is easy, until it's not.

This was the general idea behind "navigationSaga"\- to set things up so
there's as little as possible happening at a given time.

> The claims that testability is improved do not ring true to me. Some of
> those tests were just awful to write and maintain.

I dunno. On one hand, I wish there were better tools for testing sagas, but on
the other, testing long chains of Promises has caused me all kinds of
headaches in the past, as well. I find that it's easier for me to keep
responsibilities separate when using sagas than it is using promises or
suchlike.

And for what it's worth, I'm not trying to bill sagas as a be-all-end-all
solution for all life's problems. :) I was just trying to sorta explicate an
approach that worked for us.

~~~
eric_b
It sounds like you guys made the right moves - trying promises first until
that fell apart, and then carefully picking a new path forward.

My comment was mainly intended as a "cautionary tale" for those folks who get
really excited about the latest and greatest, and don't apply that base level
of skepticism to new tech.

------
acemarke
This is an excellent series of posts! Parts 1 and 2 give a ground-up
description of the core concepts that redux-saga uses (generators, describing
effects, etc), and Part 3 covers some real-world use cases. One of the best
tutorials on redux-saga I've seen.

If anyone's interested, I do have links to other tutorials and articles on
redux-saga in my React/Redux links list, at
[https://github.com/markerikson/react-redux-
links/blob/master...](https://github.com/markerikson/react-redux-
links/blob/master/redux-side-effects.md#sagas) .

------
inglor
A whole class of problems you wouldn't have in the first place with MobX. So
much code in the article for doing so little - where good design could solve
the issues much more elegantly using plain function composition and JS
concepts.

~~~
mhink
Author here.

As I mentioned in other posts, I don't believe redux-saga is the be-all-end-
all solution to every problem in client-side Javascript. It's a good solution
for codebases which need to coordinate among several different asynchronous
processes.

And for what it's worth, I wanted to err on the side of verbosity. I find that
a lot of blog posts kinda elide over too much, and so I was actively trying to
break down my thought process into very discrete steps. At the end of the day,
I think you'll find that the final product involves less than 300 lines of
code. If that.

On a different tack, MobX is an excellent library, and we've actually used it
to great effect at Formidable! Ken Wheeler has a great post about the topic.
The point of _this_ article, though, is simply to share something that worked
well for us on a complex project, where replacing Redux with MobX wouldn't
have been that helpful.

Along those lines, I ask you in complete honesty: what would you like to see
out of a "Javascript Power Tools: MobX" article? I'd love to dig into it a
little bit, find out what makes it tick, and share that with folks. :)

~~~
hakanito
I think it would be great for the community to see a writeup on how complex
async flows can be handled with MobX reactions and data atoms, two pretty
powerful concepts IMO

------
timbuckley
I'm going to use this opportunity to plug my Redux Saga testing library:
redux-saga-test-engine[0][1]. It makes saga tests much less verbose. Let me
know if you like it (or don't)! :)

[0] [https://github.com/DNAinfo/redux-saga-test-
engine](https://github.com/DNAinfo/redux-saga-test-engine) [1]
npmjs.com/package/redux-saga-test-engine

~~~
RussianCow
This is awesome! I'm definitely going to give it a try on my next side
project. :) Thanks for sharing!

------
k__
I don't understand.

Why not simply observables?

~~~
mhink
Author here.

Saying "why not simply observables" kinda glosses over a lot of details, but
I'll try and answer as best as I can.

I've always found it a bit difficult to use Observables in application-style
code, both in a conceptual sense (handling Redux actions as an observable
stream) and in a practical sense (actually building and debugging streams and
transformations over those streams). I do think they're excellent tools for
dealing with cross-cutting concerns like logging, analytics, and debugging.
But as I mentioned in the article, "business logic is inherently procedural,
and expressing it as such makes our intent clearer." That's the major
advantage, I think- being able to use simpler tools (e.g. loops and local
variables) in a powerful new way.

And don't get me wrong, I'd love to learn RxJS properly one of these days. :)
This article isn't about saying "well this approach is THE BEST APPROACH",
it's more about saying "These tools turned out to be super helpful. Here's my
thought process, maybe this approach will help you as well."

~~~
k__
Yes, this article assumes (rightfully) that people are using redux saga and
need help with it.

I just found the whole concept of redux and redux-saga a bit strange. It
seemed like people don't want to use observables for whatever reasons and try
to push Redux to solve these async problems.

Your article is good, I just had the feeling it wouldn't be needed if people
would use better suited abstractions in the first place.

~~~
acemarke
I'm still not sure exactly what you're trying to say there, particularly by
"push Redux to solve these async problems".

Redux itself is about synchronous state updates, as inspired by the Flux
Architecture. Because of that, async behavior and side effects are handled via
middleware.

Most developers are not overly comfortable with FP concepts or code. Redux was
intended as a lightweight intro to FP principles, and React also helps push
people in that direction. Even fewer people are comfortable with observables,
although Angular is maybe starting to change that somewhat. So, it's not just
about "better suited abstractions", it's about what developers understand and
can use well.

To be honest, your comments seem to be leaning towards the common stereotype
of an FP enthusiast: "This approach is clearly superior and correct, why would
people do anything else?"

~~~
BigJono
> Because of that, async behavior and side effects are handled via middleware.

It shouldn't be though. The middleware pattern makes a lot of sense for
transforming data (communication between mismatched APIs etc), or using it in
some way before passing it on (logging, analytics etc). IMO it's totally wrong
for this use case though.

All redux-thunk is doing is taking some "action", clobbering it, pretending it
doesn't exist, and running some arbitrary function that it's passed.

dispatch(higherOrderFunctionThatCreatesAsyncFunction())

is identical to

asyncFunction()

What people are looking for is some way to get the data store dependencies
into asyncFunction in a testable way. This can easily be done without
middleware.

~~~
acemarke
Yes, yes, we've had this argument about redux-thunk in prior threads :)

That said, it's worth noting that the explicit design goal for middleware
_was_ for async behavior, per the comments from Dan and Andrew I've quoted in
[http://blog.isquaredsoftware.com/2017/05/idiomatic-redux-
tao...](http://blog.isquaredsoftware.com/2017/05/idiomatic-redux-tao-of-redux-
part-1/) . To pick out one specific quote from Andrew:

> [the] reason the middleware API exists in the first place is because we
> explicitly did not want to prescribe a particular solution for async." My
> previous Flux library, Flummox, had what was essentially a promise
> middleware built in. It was convenient for some, but because it was built
> in, you couldn't change or opt-out of its behavior. With Redux, we knew that
> the community would come up with a multitude of better async solutions that
> whatever we could have built in ourselves.

> Redux Thunk is promoted in the docs because it's the absolute bare minimum
> solution. We were confident that the community would come up with something
> different and/or better. We were right!

~~~
BigJono
Yeah I'm under no illusions that me and Dan Abramov are operating under a
vastly different model for how we think an application should be structured.
The concept of a data store having a "solution for async" is, to me at least,
absurd. It's like asking why my fridge doesn't have a solution for next day
grocery delivery.

I don't see why my fridge needs to be concerned about whether my broccoli is
sitting on the bench ready to be put in, or still being washed down at the
green grocers.

From the fridge's point of view, a piece of broccoli is inserted into it at a
specific point of time. The contents of the fridge can be defined as an
ordered set of insertions and removals. The fridge's state depends on knowing
whether a piece of broccoli was inserted at 12:58pm or 1:03pm, since that may
change the order things were stacked in, but it doesn't need to know whether I
placed an order for that broccoli this morning, yesterday, or a week ago; or
how it arrived at my house. Making my fridge responsible for that process
would seem to violate the concept of separation of concerns.

~~~
k__
Finally someone sane, thanks.

Funny thing is that Dan himself said Redux could be implemented in 5 lines of
RxJS and was also rathere interested in observables til he met some haters on
a conf.

I remember him asking a question about observables being the next thing or
future or something and the speaker just didn't know what to say.

------
cnp
The codebase for redux-saga is a dream to read through:
[https://github.com/redux-saga/redux-saga](https://github.com/redux-
saga/redux-saga)

~~~
z3t4
could you actually get anything done in js before es6 !?

~~~
rounce
Quite happily, ES6 is 90% sugar.

~~~
elyobo
Was that a yes or a no? :D

