Are there any examples of using redux-loop
to handle complex ajax workflows? The official repo is very minimalistic. From the one example in the wild I was able to find (https://hackernoon.com/why-i-wrote-a-redux-async-outerware-277d450dba74#.2ocaloc58), it seems that redux-loop is very similar to redux-thunk.
Here are some examples of complex ajax workflows:
- Ajax Workflow1. User selects two different filters on a results table. Each filter initiates an ajax request, which then resolve out of order. The results table should show the correct filter selection. Errors should not update the results table.
- Ajax Workflow 2
- User starts a report generation (which is a long running process).
- User switches to another report. It should either cancel or ignore the pending “wait for report” action.
- More complex workflow (based on an old redux-saga example)
- User presses log-in button, which starts an ajax request to get an auth token
- Either
- User immediately presses log-out button, which should cancel/ignore the pending auth action
- OR it should store the auth token when it resolves
- Should clear the auth token after logout or if login error occurs
I will give a shot at the second workflow (login).
Before going into the code, it's worth noting
redux-loop
is a lot simpler and offers less thanredux-saga
in terms of async control flow. But in the spirit ofElm
, the focus is on data flow - not surprisingly is typically achieved by data types. Therefore it is helpful to think from the perspective of a statically typed language. InHaskell
orElm
, it's probably beneficial to model the problem by data type, which itself encodes a state machine:Where
data
anderr
are type variables represent login data type (tokens) and login errors. JavaScript being dynamically typed, does not have the benefit expressing the same idea - but there are a lot of dynamic tricks one can use to mimic tagged union types likeLoginStatus
. Without further ago, here's the code:Here I will use a simple and lesser known library singe-key to achieve very basic run time union types. A "single-key" object as it's name suggest, is an object with just a key and a value, such as
{ a: 1 }
("a" is the key, and 1 is the value). We shall model the state with single-key objects - different keys represent different variants ofLoginStatus
. A few example states:With that cleared up, here are the sub-reducers used in the main reducer:
These are like transitions in a finite state machine, except sometimes with data attached to the state. Each individual reducers are fairly simple and handles very few action types. Only two reducers return effects:
This transitions the state from
LoggedOut
/LoginError
toPending
and specify some side effects - which will be scheduled byredux-loop
. You may even merge the two variants into one:{ LoggedOut : error | null }
, but I feel having a seperateLoginError
state is beneficial in the long run.With some notion of data types, this problem can be easier to reason about than it first appears; you can do the same thing with reducer structured roughly the same and use just
redux-thunk
.