redux-track-promise
A tiny redux library with a reducer that handles setPending
, resolve
, and reject
actions, and a trackPromise
method that takes a Promise
and dispatches those actions when the promise state changes.
I used to write a lot of similar reducers and action creators for keeping track of asynchronous operations, but I got tired of repeating myself and wrote this library to use in all of those cases.
Quickstart
Let's say you want to track the status of some HTTPS requests your redux state. Here's all it takes to set that up:
const track: trackLoginPromise reducer: loginPromiseReducer = const track: trackSetPasswordPromise reducer: setPasswordPromiseReducer = const reducer = const store =
Then whenever you make an HTTPS request, call one of your track*Promise
functions to sync that promise into redux:
Calling trackLoginPromise
will dispatch a LOGIN_PROMISE.SET_PENDING
that will update state.loginPromise
.
Then later it will dispatch a LOGIN_PROMISE.RESOLVE
or LOGIN_PROMISE.REJECT
action, depending on whether the promise
gets resolved or rejected.
Likewise, trackSetPasswordPromise
will dispatch SET_PASSWORD_PROMISE.*
actions that update
state.setPasswordPromise
.
UI Integration
Displaying the status of the requests is easy! You can create a generic PromiseStatus
component that
provides a consistent UI for anywhere in your app you're displaying the status of a promise:
const PromiseStatus = { if pending return <div className="alert alert-info"><span className="spinner"> messagespending</div> if fulfilled return <div className="alert alert-success">messagesfulfilled</div> if rejected return <div className="alert alert-danger">messagesrejected reasonmessage</div> return <span />}
Then customize it, connect it to the promise state, and use it in your views like this:
const LoginStatus = <<<<<<< HEADconst LoginView = <form onSubmit= { e } > <LoginStatus /> ... </form> const SetPasswordStatus = const SetPasswordView = <form onSubmit= { e } > <SetPasswordStatus /> ... </form>
What the state looks like
Initial (no promise) | Pending | Fulfilled | Rejected |
---|---|---|---|
{
pending: false,
fulfilled: false,
rejected: false,
value: null,
reason: null,
}
|
{
pending: true,
fulfilled: false,
rejected: false,
value: null,
reason: null,
}
|
{
pending: false,
fulfilled: true,
rejected: false,
value: {...},
reason: null,
}
|
{
pending: false,
fulfilled: false,
rejected: true,
value: null,
reason: Error(...),
}
|
Dispatching actions manually
const setPending resolve reject =
(Of course, you could just use es6-promisify
on a method that takes a callback and pass its promise to
trackPromise
. You may prefer to dispatch actions manually if your async operation involves more than a single
function call.)
Resetting to initial, no promise state
You might want to hide any pending/resolved/rejected status in your UI altogether.
To do that, dispatch setPending(false)
:
const setPending =
How to rename all action types, action creators, and state fields
Let's say you want to track the state of a Meteor subscription. Using this library makes total sense: pending
can mean the subscription is sending initial data, fulfilled
can mean it's ready, and rejected
can mean it stopped.
But what if you want the names of these fields and the action types to match Meteor terminology?
Here's how you could do it:
{ const setPending resolve reject track reducer = return setInitializing: setPending setReady: resolve setStopped: reject track { const pending fulfilled rejected reason = return initializing: pending ready: fulfilled stopped: rejected error: reason } } const setInitializing setReady setStopped reducer = // now state is {initializing: false, ready: true, stopped: false, error: null}