redux-pack
redux-pack copied to clipboard
[Question] How to organize complex scenarios
How would you organize complex scenarios involving multiple async stuff and multiple action types?
With redux-saga its superb easy once one gets comfortable with generators and with redux-observable for a skillful observable user it should be quite easy too (although didnt use the latter personally).
Wondering how to orchestrating smth more complex would look like with the proposed approach of those self-contained descriptive actions in redux-pack.
Would be cool if such example be in the docs.
Nice lib nonetheless!
Hey @Andarist, thanks for asking.
I'm open to discussing newer/better ways to solve complex flows. For now, one escape hatch is to use the promise returned from dispatch in concert with redux-thunk:
function doSomething() {
return dispatch => {
dispatch({
type: FOO,
promise: Api.getFoo()
}).then(({ success, payload }) => {
if (success) {
// do something complicated with dispatch that you want to happen when promise succeeds
} else {
// do something complicated on failure
}
});
};
}
Are you using this pattern extensively within airbnb? It seems like it can introduce much promise hell for complex stuff On Thu, 15 Dec 2016 at 01:13, Leland Richardson [email protected] wrote:
Hey @Andarist https://github.com/Andarist, thanks for asking.
I'm open to discussing newer/better ways to solve complex flows. For now, one escape hatch is to use the promise returned from dispatch in concert with redux-thunk:
function doSomething() { return dispatch => { dispatch({ type: FOO, promise: Api.getFoo() }).then(({ success, payload }) => { if (success) { // do something complicated with dispatch that you want to happen when promise succeeds } else { // do something complicated on failure } }); }; }
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/lelandrichardson/redux-pack/issues/3#issuecomment-267197523, or mute the thread https://github.com/notifications/unsubscribe-auth/AJWMkn42uCW9xUMwKf_bLwt436HoJZNOks5rIIYdgaJpZM4LNgcO .
@Andarist it is used in our entire RN code base now, but we are still learning about what kind of complex use cases it is not ideal for. It's worth noting that you can use whatever other middleware (like redux-thunk) for really complicated flows.
Personally, I don't think this introduces any more promise hell than would already exist?
If you have a specific use case, I'd love to hear it.
For example something like this:
- each action A should trigger an asynchronous task
- but depending on some action props (other than type) only the latest spawned task should be completed
To illustrate
store.dispatch({ type: A, id: 1 }) // ignored
store.dispatch({ type: A, id: 2 }) // ignored
store.dispatch({ type: A, id: 2 }) // successful
store.dispatch({ type: A, id: 1 }) // ignored
store.dispatch({ type: A, id: 1 }) // successful
Or such as:
- action A triggers ability to perform 2 different actions for time being
- so once dispatched we need to open a time window for accepting 2 other action types
- only first accepted action should perform its task
Ofc I see how to handle it with closures, some maps etc but that seems hard to follow later
@lelandrichardson Thanks for the wonderful library.It really helps in writing more maintainable code. So I am stuck in a situation where I need to dispatch two actions one after another wherein a part (id of the newly created entity) of the response from first API call (POST request) is used as request data for the second API call (Another POST request that uses the retrieved id ).How do you deal with such scenarios?
Wondering if the best practice here would be to use redux-pack with a 1-1 action=>promise relationship - ie. no mixing with redux-thunk etc and no chaining - each action does one thing.
export const doSomething = () => ({
type: FOO,
promise: Api.getFoo(),
})
export const doSomethingElse = () => ({
type: BAR,
promise: Api.getBar(),
})
And then use middleware ✨ to accomplish complex flows that require chaining of actions. This is ultimately the purpose of middleware - to react to dispatched actions - and helps maintain a logical flow of actions/state.
const FooMiddleware = ({ dispatch, getState }) => next => action => {
if ( action.type === 'FOO' && action.payload && !action.error ) {
// `FOO` succeeded, do something else now!
dispatch(doSomethingElse())
}
return next(action)
}
@Andarist Not fully related to this repo, but I think bare promises are sufficient for all patterns (can be enhanced with async when needed), and falling back to the more verbose redux-thunk: args => (dispatch (, getState)) => { ..} if really needed
I often use this small promise middleware after redux-thunk: https://gist.github.com/caub/556a04f4a2a1d0af326e99b593916aa2, I added at the end some usecases comparisons
@lelandrichardson it would seem to me that your comment is more than an escape hatch. It makes the onSuccess and onFailure callbacks redundant. More importantly it is easier to test than those callbacks. Do you agree? Is there anything else to consider before I implement the "escape hatch" everywhere in my app?
Thanks for this library by the way.
@divyanshutomar
Thanks for the wonderful library.It really helps in writing more maintainable code. So I am stuck in a situation where I need to dispatch two actions one after another wherein a part (id of the newly created entity) of the response from first API call (POST request) is used as request data for the second API call (Another POST request that uses the retrieved id ). How do you deal with such scenarios?
I think maybe a good solution is to chain the promises given that this is a transaction one thing shouldn't update the state without the other. So this could be done in the following way:
const getFooAndThenGetBar = async () => {
const foo = await API.getFoo();
const bar = await API.getBar();
return { foo, bar };
}
export const doSomething = () => ({
type: FOO_AND_BAR,
promise: getFooAndThenGetBar(),
})
Then you can actually listen for FOO_AND_BAR in the 2 reducers that interest you.