
Two Weird Tricks with Redux - jlongster
http://jlongster.com/Two-Weird-Tricks-with-Redux
======
tlrobinson
I like the elegant simplicity of Redux, but it's so low-level and definitely
feels like people are still figuring the "right" way to do a lot of things.

I tweeted my feelings on this last week:

    
    
        2015: reflux flummox redux marty alt fluxible…
        2016: redux-actions redux-act redux-forms redux-promise redux-ui…
    

[https://twitter.com/tlrobinson/status/717258992989306880](https://twitter.com/tlrobinson/status/717258992989306880)

~~~
acemarke
Heh. Can definitely vouch for that. I recently put together a links repo
cataloging most of the Redux-related ecosystem, and was amazed at how many
overlapping or similar libraries there were. 12 different promise middlewares,
a couple dozen different side effects plugins, and I don't know _how_ many
utilities to generate action creators, reducers, and constants.

The list is over at [https://github.com/markerikson/redux-ecosystem-
links](https://github.com/markerikson/redux-ecosystem-links) if you want to
look.

~~~
33degrees
Thanks for putting that together! I'd discovered it a while ago and have been
checking out all the utility libraries, trying to find ones that work the way
I want. The funny thing is that so far none do, so I'm now working on one of
my own...

------
BinaryIdiot
I don't know much about Redux but these tricks seem like they should be less
trick and more re-worked flow.

For the first one, if a burger is meant to be consumed differently than fries
(serial versus parallel) they why are they using the same mechanism with the
burger having a hacked on predicate , service type, dispatch, etc? Why
wouldn't the burger just have a workflow that makes sense for how its invoked?
For instance if the burger is tied to the UI and a backend request that must
happen one at a time then the UI would disallow or provide a way of queueing
which I guess this sorta supports but it seems forced and doesn't look like a
normal flow.

The second part, ignoring async responses. I don't understand this part. Why
can't you just replace your state and drop the existing handlers so nothing
async from the previous state occurs? Does redux just not support this out of
the box or is the author doing something very edge-case-y?

When I don't want to handle an async response anymore I remove the handle's
association with the event it was going to handle. It works like this in
native DOM, jQuery, even my own bias data point: msngr. But I'm curious why
wouldn't redux be able to handle the same case?

~~~
jlongster
There are many reasons why the UI should not care about how the server
requests are ordered.

> When I don't want to handle an async response anymore I remove the handle's
> association with the event it was going to handle. It works like this in
> native DOM, jQuery, even my own bias data point: msngr. But I'm curious why
> wouldn't redux be able to handle the same case?

In our case we use the same connection but it switches "contexts" and we want
to ignore everything from a previous context. In simpler situations you could
make sure the your connection is destroyed before the UI is destroyed, yes.

~~~
BinaryIdiot
> There are many reasons why the UI should not care about how the server
> requests are ordered.

Sure. It was just an example. My main point was the change here felt like a
hack and they forced a mechanism that should be processed serially into one
that is meant to handle parallel.

> In our case we use the same connection but it switches "contexts" and we
> want to ignore everything from a previous context. In simpler situations you
> could make sure the your connection is destroyed before the UI is destroyed,
> yes.

So wouldn't it be best to sever ties that will cause issues and reconnect them
versus anything else? That's typically the pattern you use when you want to
replace event handlers. The way in the article just wastes a lot of CPU cycles
to support an awkward pattern I've never seen someone use before.

Just my 2 cents. But like I said I've never used Redux so maybe these patterns
are normal here.

------
mdc2161
I just started with redux-saga which uses ES6 generators to handle app control
flow.

After first impressions, I'd say it's worth a look if you're running into
anything near as complicated as talked about here. It's very easy to reason
about and test. gaearon - the creator of redux - seems to approve of it as
well.

[https://github.com/yelouafi/redux-saga](https://github.com/yelouafi/redux-
saga)

~~~
troebr
I just wrote our API layer with redux-saga too, I started with thunks but I
didn't like dispatching thunks and actions, also it was hard to reason about
flow and side effects: If I update my search form, the results need to be
updated, but the action is "update form", not "update form and update search
results", the search result update is a side effect more than something that
should be actually fired from the update form.

Saga is making side-effects (workflows) possible by subscribing to events.

So in this case I have a saga waiting for 'SEARCH_FORM_UPDATE', then it waits
for 300ms, then starts the ajax request flow: a request start action, then a
request complete action (or request error).

The code reads almost like synchronous code once you understand yield.

It also allows you to listen to actions that you do not control: we wanted to
do something whenever a specific redux-router action happened. You can't do
this with thunks, we would have needed a middleware to do this. With sagas you
can simply hook up a flow to the action.

------
raxis
The isEatingBurger (isLoading) flag seems to be idiomatic in Redux and shows
up in tutorials. In addition to preventing overlapping requests, it's useful
for spinners, disabling submit buttons, etc. It's cool that you rolled the
isLoading pattern into a service.

If you really want to pass around the promise, it's doable. To save in your
component state, return the promise from your thunk so it gets returned by
dispatch (thom_nic just posted this also). To save in your store, dispatch a
'save' action from the thunk with the promise as an argument. But I agree that
it's probably a bad design.

The request sequence IDs are useful for many situations where you need to
order or cancel async requests. If you have multiple in-flight requests for an
autocomplete field, you only want to set isLoading=false when the last request
completes, and you don't want the result of a later request to overwrite the
result of an earlier request if the responses come back out-of-order.

------
indeyets
My usual approach is to avoid middlewares for async-requests and to keep this
stuff in a separate layer. For example, it can look like an ApiClient object
with ES7 async-methods. Such methods would:

1\. send "Start" action

2\. await for result of
fetch("[http://example.com/api/call");](http://example.com/api/call"\);)

3\. send success or error action

this way redux is 100% synchronous and I have an obvious place for throttling
requests, keeping trace of promises, etc.

------
emilong
In addition to redux-saga ([https://github.com/yelouafi/redux-
saga](https://github.com/yelouafi/redux-saga)) which others have mentioned,
redux-loop ([https://github.com/raisemarketplace/redux-
loop](https://github.com/raisemarketplace/redux-loop)) is another interesting
"declarative" approach to sequencing operations that uses promises. I'm not
using either (yet), but I wrote up my notes on the differences between them
and the thunk middleware approach recently
([https://blog.boldlisting.com/connecting-redux-to-your-api-
ea...](https://blog.boldlisting.com/connecting-redux-to-your-api-
eac51ad9ff89#.79d5lnivt)). One aspect of these discussions that I find is lost
or at least very implicit is the notion of storing metadata about the status
of async operations as well as their result is elided together, but there's
reason to make mindful choices about whether or not to do that.

BTW, the "Ignoring Async Responses" section here is a useful pattern, even if
you're not working on browser developer tools ;). I've been working on a web
app and have a polling timeout that I cancel on logout. Not quite the same,
but logout triggers a reset of all the state obtained while authenticated, but
the timeout ids are also in the store and canceled before being cleared.

------
rbalicki
What stuck out to me is

"In an asynchronous world, you have 3 actions indicating asynchronous work:
start, done, and error. In our system, they all are of the same type (like
ADD_BREAKPOINT) but the status field indicates the event type."

This is a great suggestion (I have used a very similar schema before, although
in flux), and should be the standard suggestion in redux tutorials.

Not enforcing this can lead to a proliferation of inconsistently named
constants, especially if a team is learning flux/redux and has not yet settled
on a naming schema.

~~~
bdavisx
Check out flux standard actions for even more consistency:

[https://github.com/acdlite/flux-standard-
action](https://github.com/acdlite/flux-standard-action)

~~~
renke1
One problem I have with standard action is that on failure I sometimes want to
have more than just an Error as payload. Something like `payload: {error,
seqId}`. You could add it to error itself, but I find it more consistent to
put it into the payload directly.

------
dyscrete
Can you tell me a use case using the "action" that's sent to "request.run", or
is it passed just because it can be.

[https://github.com/mozilla/gecko-
dev/blob/master/devtools/cl...](https://github.com/mozilla/gecko-
dev/blob/master/devtools/client/shared/redux/middleware/wait-service.js#L51)

~~~
jlongster
If you are waiting for a "done" async action (which is usually the case if
using this), you might want the action to inspect the value that it was
resolved with. We use this in tests: [https://github.com/mozilla/gecko-
dev/blob/master/devtools/cl...](https://github.com/mozilla/gecko-
dev/blob/master/devtools/client/debugger/test/mochitest/head.js#L1234)

Tests can wait for an action to be dispatched and the promise is resolved with
that action, and tests can inspect it.

------
shopfaunt321
Good stuff,

The EAT_BURGER seems slightly odd, as i've never hit a situation where I would
allow a second one to be fired while the first is still pending. I.E. upon
EAT_BURGER.start i would disable the UI for the EAT_BURGER action to be fired.
I guess that's an idealised world and you've hit something more complicated
that requires it though, and the solution is good

I particularly like the async call tracking - very simple and neatly solves
your issue!

Cheers

------
thom_nic
re: an action caller needing to wait on the async action to complete: I
thought that was a non-issue. If you use redux-thunk and your action creator
returns a promise, the caller gets it. So while you're no longer triggering
off of a redux action/store state change -- which may or may not be seen as a
good thing -- you _can_ get the promise and add a `.then()` which will fire
after the async action completes.

I put together a simple example here: [https://gist.github.com/thom-
nic/fbedf27a5222a8490aa9028a40b...](https://gist.github.com/thom-
nic/fbedf27a5222a8490aa9028a40bf053c)

Basically: component fires a 'delete' action, and then does page navigation
when delete is successful. (Not saying this is a good idea to do it that way,
just that you _can_ do it.) Or did I miss his point?

~~~
jlongster
My post says exactly this. Read the full thing. There are other cases where
you do not have a reference to the promise. (think a function being called
again at some indeterminate time)

~~~
thom_nic
Ok, sorry for the misunderstanding - I'll re-read the post and see if I can
understand the scenario you're describing.

------
Kiro
> I’ve used it a couple times to solve complex scenarios

I would love to know what those scenarios were.

~~~
jlongster
Basically a UI optimization. Instead of firing multiple requests to the server
a lot, we know that a specific UI event may happen multiple times in a short
period, so we "throttle" it by only performing the request once at a time and
only re-requesting it if it was requested again.

So far it's always been a UI optimization like that.

You could handle this at the connection layer, but I liked how it integrates
with actions better (it gives a finer control over when actions are
dispatched)

------
cel1ne
Why not keeping Async operations as react-elements (simple <div/>s with
hookds) in the DOM?

------
theknarf
If I need something with more features than Redux I would probably just use
Rx.js.

