This is the one thing that I haven't found a standard solution to yet.
I have my store setup with redux-saga for side effect handling, I dispatch an action (that has async side effects) from a component, and now want the component to do something once the side effects are handled (for example navigate to another route/screen, popup a modal/toast or anything else).
Also I want to display a loading indicator or any errors on failure.
Before redux, this kind of flow was straight forward, it'd look something like this:
try {
this.setState({loading: true});
const result = await doSomeRequest();
this.setState({item: result, loading: false});
} catch (e) {
this.setState({loading: false, error: e});
}
With redux, I'd typically want to dispatch an action initiating the flow and have all related information in the store, to allow many components to listen to what is happening.
I could have 3 actions, 'requested', 'success', 'failed'. In my component I would dispatch the requested action. The responsible saga will then dispatch either the 'success' or 'failed' action upon handling 'requested'.
My Component will reflect on the changes. But I haven't found out a standard way to figure out if the action has completed. Maybe the store hasn't updated as result of the async action (NO-OP, but loading state would still change I guess). But the action still succeeded, and I want to do something like navigate to another screen.
I really tried finding this kind of (seemingly common) scenario in the redux docs, redux-saga docs or Stackoverflow/Google, but no success.
Note: also with redux-thunk I think this behaviour is straight forward to achieve, since I can just .then on an async action dispatch and would receive the success action or the error action in catch (correct me if I'm wrong, never really used thunk). But I haven't seen the same behaviour achieved with redux-saga yet.
I've come up with 3 concrete solutions:
Most primitive solution, handling only the 'success'/'failed' actions from the component. Now this solution I am not a big fan of. In my concrete implementation there is no action that indicates that the async request has been started. The side effects are handled right there in the Component, instead of being abstracted away within a saga. Lots of potential code repitition.
Running a one time saga right before dispatching the request action, that races the 'success'/'failed' actions against each other and allows to react on the first occurring action. For this I've written a helper that abstracts the running of the saga away: https://github.com/milanju/redux-post-handling-example/blob/master/src/watchNext.js This example I like a lot more than 1. since it's simple and declarative. Though I don't know if creating a saga during run time like this has any negative consequences, or maybe there is another 'proper' way to achieve what I'm doing with redux-saga?
Putting everything related to the action (loading, successFlag, error) into the store, and reacting in componentWillReceiveProps on action changes ((!this.props.success && nextProps.success)) means the action has completed successful). This is similar to the second example, but works with whatever side effect handling solution you choose. Maybe I'm overseeing something like the detection of an action succeeding not working if props hail in very fast and props going into componentWillReceiveProps will 'pile up' and the component skips the transition from non-success to success altogether?
Please feel free to have a look at the example project I've created for this question, that has the full example solutions implemented: https://github.com/milanju/redux-post-handling-example
I would love some input on the methods I use to handle the described flow of actions.
- Am I misunderstanding something here? The 'solutions' I came up with were not straight forward to me at all. Maybe I'm looking at the problem from the wrong angle.
- Are there any issues with the examples above?
- Are there any best practice or standard solutions for this problem?
- How do you handle the described flow?
Thanks for reading.