This redux middleware allows dispatching async actions, that may include:
- promise
- array of promises, that will be chained
- functions returning promises
- array of functions returning prommises, that will be chained
- array of other async actions, that will be chained
- functions returning anything, that will be chained
- array of functions returning anything, that will be chained
It will handle errors in promises, dispatching ERROR actions. Exceptions, that occured in your reducer won't be swallowed and will be thrown.
Lets say, you have some functions, returning promises for web api requests:
const apiLogin = password => new Promise((resolve, reject) => {
setTimeout(() => {
resolve(15921);
}, 1000);
});
const apiPullUserProfile = userId => new Promise((resolve, reject) => {
if (userId === 15921) {
setTimeout(() => {
resolve({
avatarUrl: "https://img.example.org/15921/Gc2xcYMvRieoA8.png"
});
}, 1000);
}
});
const apiPullAvatar = avatarUrl => new Promise((resolve, reject) => {
if (avatarUrl === "https://img.example.org/15921/Gc2xcYMvRieoA8.png") {
setTimeout(() => {
resolve("Done");
}, 1000);
}
});
You can make corresponding actions creators:
const startLoggingIn = password => ({
types: ["LOGGING_IN_STARTED", "LOGGING_IN_SUCCESS", "LOGGING_IN_ERROR"],
payload: apiLogin(password)
});
const startPullingProfile = userId => ({
types: ["PULLING_PROFILE_STARTED", "PULLING_PROFILE_SUCCESS", "PULLING_PROFILE_ERROR"],
payload: apiLogin(userId)
});
const startPullingAvatar = avatarUrl => ({
types: ["PULLING_AVATAR_STARTED", "PULLING_AVATAR_SUCCESS", "PULLING_AVATAR_ERROR"],
payload: apiLogin(avatarUrl)
});
Now you can make one action creator to rule them all:
const startEverything = password => ({
types: ["EVERYTHING_STARTED", "EVERYTHING_SUCCESS", "EVERYTHING_ERROR"],
payload: [
startLoggingIn(password),
startPullingProfile,
startPullingAvatar
]
});
That will results in creating actions in the following order:
{ type: EVERYTHING_STARTED }
{ type: LOGGING_IN_STARTED }
{ type: LOGGING_IN_SUCCESS, payload: 15921 /* userId */ }
{ type: PULLING_PROFILE_STARTED }
{ type: PULLING_PROFILE_SUCCESS, payload: "https://img.example.org/15921/Gc2xcYMvRieoA8.png" /* avatarUrl */ }
{ type: PULLING_AVATAR_STARTED }
{ type: PULLING_AVATAR_SUCCESS, payload: /* avatar */ }
{ type: EVERYTHING_SUCCESS }
You can transform data in the midle of chain, lets replace https
with http
:
const startEverything = password => ({
types: ["EVERYTHING_STARTED", "EVERYTHING_SUCCESS", "EVERYTHING_ERROR"],
payload: [
startLoggingIn(password),
startPullingProfile,
url => url.replace("https", "http"),
startPullingAvatar
]
});
// Or even asynchronously:
const startEverything = password => ({
types: ["EVERYTHING_STARTED", "EVERYTHING_SUCCESS", "EVERYTHING_ERROR"],
payload: [
startLoggingIn(password),
startPullingProfile,
url => new Promise((resolve, reject) => {
setTimeout(() => {
resolve(url.replace("https", "http"));
}, 500);
}),
startPullingAvatar
]
});
Any questions, propositions, pull requests are welcome in issues.