React + Flux: best practice for calling an action after another action? - reactjs

I have a component, let's call it List, that calls an action creator Action1.getItems().
After Action1.getItems() has retrieved information from a server, I need to call another action creator: Action2.getMoreItems(), which is a totally different action in a different file.
Facts:
Both actions creators dispatch an action and change a store.
I can't call Action2.getMoreItems() inside the _onChange() of List because it throws an error "Cannot dispatch in the middle of a dispatch ..."
Possibilities?
Call Action2.getMoreItems() inside Action1.getItems() after the server returns
Add a callback so I will have Action1.getItems(callback) and call Action2.getMoreItems() inside that callback from List component.
I believe this might be a very basic use of Flux but I still can't get my head around on how to get it right, both solutions look ugly to me.
Suggestions?
Thank you!

Is getMoreItems dependent on the return value of getItems? If so, you can call the action in the .then callback of getItems.
If it's not, then you can invoke both calls at the same time and then concat the results of the promises.
So my vote is number 2 in your question. I don't think it's an ugly solution at all. You are delegating the responsibility of getting items and more items to your actions. The component itself is ignorant of that and simply dispatched a request for those items. How they're built should be transparent to the component.

Most of the flux implementations have a method like "waitFor", which lets a store wait for another store to finish.
Assuming you need two execute 2 methods in your store: GetUser and GetPermissions. The first fetches a user and the latter the permissions, the fetched user has. Obviously, GetPermissions needs to wait for the user object to be fetched from the first method.
In this case, you would dispatch an action called GetUser and make both of your store functions listen to that action. So if you dispatch GetUser both store methods are executed.
In alt.js (flux implementation) and in many others, there are methods like waitFor(store), so GetPermissions can wait for the UserStore to fetch the user. In alt.js pseudocode this would look like this:
// UserStore
function getUser() {
fetch('api/user').then(this.setState);
}
// PermissionsStore
function getPermissions() {
this.waitFor(UserStore);
let user = UserStore.getState().user;
fetch('api/permission', user).then(this.setState);
}
This is, as I said, not working pseudocode, but it shows the concept. If you are interested, the flux implementation alt.js is great. Check it out at alt.js.org

Related

React/Redux how to wait for dispatched action to complete?

In my thunk, I have two dispatches in an array called dispatches which modify the Redux store. Only after both of them have completed (i.e., modified the Redux store), I want to dispatch finalDispatchCall. I tried putting them both in a Promise and called then, but I still see the finalDispatchCall being made before the secondDispatchCall has updated the Redux store.
const dispatches = [];
dispatches.push(dispatch(firstDispatchCall());
dispatches.push(dispatch(secondDispatchCall());
Promise.all([...dispatches]).then(() => {
dispatch(finalDispatchCall());
})
.catch(error => {
logger.error(
`Print out error - ${error}`
);
});
Is there a way to make sure a dispatch has completed before calling another dispatch?
EDIT (more details):
The firstDispatchCall is making an API call (returning fetch) and dispatching an action in the then statement to update the redux store. The secondDispatchCall is also making an API call (returning fetch) and dispatching two other actions in their then statements which each make their own API calls and dispatch actions to update the redux store.
I want to wait until all of this is complete before making that finalDispatchCall.
As long as firstDispatchCall() and secondDispatchCall() are sync, then redux guarantees that they'll arrive synchronously, in sequence, and that subsequent calls to dispatch() will act upon the updated state. But, if you want to be absolutely certain, you can use store.subscribe(), to kind of procedurally await all the changes you're expecting, and read the store on each change to confirm they're all there. This will actually be essential if first and second DispatchCall are async.
But, the question is whether you really need to know, from that specific thunk, when all the dispatches have landed. Redux is an event bus, and as such offers many ways to skin the async cat. It might help to understand more about the problem you're trying to solve by waiting for these dispatches in order to dispatch the last one. It may be that you don't really need to do that...
I recommend reading the Redux team's take on this exact question: "Should I dispatch multiple actions in a row from one action creator?". Especially note there:
"In general, ask if these actions are related but independent, or
should actually be represented as one action."
Newcomers to Redux often use multiple actions for what could be a single action, because they forget that multiple reducers can all listen to the same action.

Redux chaining asynchronous actions with redux-thunk

Say there are 2 date picker objects that corresponds to start date and end date.
These exist as states, so in order to set them, you would have to call setStartDate or setEndDate which then reducer picks up and simply sets the states.
Say I also have an async action that Is called getHugeListFromServer with (REQUEST, SUCCESS, FAILURE) and is called with parameters getHugeListFromServer(startDate, endDate).
Everytime a user picks a date, I want to call the getHugeList function and update the list. If I do something like this, it does not work because the backend action is called before the date variable is set by reducer.
example_container.jsx
class example extends React.Component {
... Methods
onChangeStartDate (startDate) {
this.props.setStartDate(startDate);
this.props.getHugeListFromServer(startDate, this.props.example.endDate);
}
// similar method for endDate.
}
I have redux thunk installed and it still doesn't help my problem. I thought about having the reducer for setDate actions to return a promise and resolve it in the container before calling the backend action, but I bet there's a better way to go about this.
Can someone lay a hand? Thanks!
I thought about having the reducer for setDate actions to return a promise and resolve it in the container before calling the backend action, but I bet there's a better way to go about this.
I don't think that sounds unreasonable at all. In fact, with redux, that would probably be completely fine and possibly preferred.
However, if you want, you could look into some lifecycle methods of components such as componentWillUpdate() - this will call each time your state is about to be updated. So you could keep your handler as is but just remove this.props.getHugeListFromServer(...) from it and put it inside of thecomponentWillUpdate() hook - check the documentation link below to see examples of how to implement it.
https://developmentarc.gitbooks.io/react-indepth/content/life_cycle/update/tapping_into_componentwillupdate.html

How to dispatch multiple actions one after another

In my react / redux application I often want to dispatch multiple actions after another.
Given the following example: After a successful login I want to store the user data and after that I want to initiate another async action that loads application configuration from the server.
I know that I can use redux-thunkto build an action creator like this
function loginRequestSuccess(data) {
return function(dispatch) {
dispatch(saveUserData(data.user))
dispatch(loadConfig())
}
}
So my questions are:
When the first dispatchreturns, is the state already changed by all reducers listening for that action? I'm wondering if the two dispatch calls are run strictly sequential.
Is this approach considered best practice for dispatching multiple actions? If not, what would you suggest alternatively?
Thanks for any help!
Yes redux-thunk allows you to do as you say, and yes it is a best practice for dispatching multiple actions (one that I prefer over the alternatives because of it's simplicity). The state is indeed updated as soon as you dispatch (by the time it returns), which is why you are given a function to retrieve the state in a thunk, instead of simply a reference to the state. In that way you can alter the state with one dispatch, and then call getState() to get a reference to the new state to read from if you need to.
redux-thunk is exactly what you are looking for. I consider it very good practice.
To answer your question, indeed when the first dispatch returns the state is already changed by the called reducers.
I know its a late response :-P You can do this by using Redux-Thunk and also checkout Redux-Saga. Redux Saga provides an amazing way to handle loads of actions at the same time. Below is an example of dispatching multiple actions using Redux-Thunk
function getAndLoadSider(moduleName){
return function(dispatch){
return doSomeApiCall(someData).then(function(item){
let _item = someOtherFunction(item.collections);
let _menuData = {
name: item.name,
key: item.key
};
return new Promise(function(res, rej){
dispatch(actionOne(_menuData));
res(_item);
}).then((_item) => dispatch(actionTwo(_item)))
}
}
Above method works well for your case when one action is dependent on the first. This is a promise based approach. If you don't like to do a lots of Promise coding I recommend you go for Sagas. Check out this link https://redux-saga.js.org/docs/advanced/ComposingSagas.html where you will learn how to compose sagas. Learning curve is steep; but once you are done, Sagas will make you a ninja redux dev.
Hope this helps :-)

How to dispatch an action when another synchronous action is complete in Redux

I have an action that updates some search criteria in Redux. I'd like to immediately fetch some data from an API based on that local criteria. The problem is that although the search criteria action dispatch is synchronous, it will not complete before the search results dispatch begins.
In this example, the criteria will not be updated before the api call:
this.props.dispatch(updateLocalCriteria(someData));
this.props.dispatch(fetchApiResults(this.props.criteria))
Am I missing something obvious? I can think of a few hacky ways to handle this, but what is the best way? Should I set a flag when search criteria updates, and fetch results in componentDidUpdate() if the flag is true? Or should I make updateLocalCriteria() asynchronous just so I can chain fetchApiResults() in the promise chain?
EDIT
I think I've found my answer. I can get the current state within fetchApiResults(). Therefore, I can get the current search criteria before making the api call.
function fetchApiResults() {
return (dispatch, getState) => {
const state = getState();
// fetch data and dispatch...
In your updateLocalCriteria, you probably already change one of the props of your reducer to the new value, I'll call it criteria.
What you could do instead is create another action let's say updateCriteriaAndFetch that will be called just like you did:
this.props.dispatch(updateCriteriaAndFetch(newCriteria))
That you will define basically like so
export const updateCriteriaAndFetch = criteria => dispatch => {
dispatch(updateLocalCriteria(criteria))
dispatch(fetchApiResults(criteria))
}
The second dispatch will even wait for your first synchronous update to be finished, even if you don't really need it because you already has access to it.
You can still do what you said, get the current state of your store, but it seems quite overkill to me, since you can simply pass it as a parameter or you action.

Handling multiple async calls with flux

I have a Calendar component which, when rendered with a certain prop called LoadOnMount needs to call the server to load a set of flights.
The problem is that I update the calendar by listening to the CalendarStore's Events.UPDATE event, which is triggered by the UPDATE_CALENDAR action dispatched by my Dispatcher, and I load the set of flights using another action called LOAD_FLIGHT_LIST. So when I invoke this new action from the Calendar component's ComponentDidMount function I get the cannot dispatch in the middle of a dispatch error.
Any way to handle these sorts of dependencies? (The calendar is also fetched from the server)
You have two issues that I can identify:
The first is that you are trying to get the dispatcher to dispatch during a dispatch. That's not the way you should be doing it.
The second is that you seem to be performing AJAX/async calls from inside your dispatch handler. I don't want to say that you should never do that, but that does not seem to be necessary in your application.
Here's a link to another stack overflow question that I think is similar: https://stackoverflow.com/a/23637463/2152216
The difference is that the asker is trying to perform an Ajax call from within his dispatch handler, while you seem to be trying to dispatch an event that will in turn trigger an ajax call during the event's handling.
What you can do is create an action that asynchronously loads the flight list, then dispatch the FLIGHT_LIST_LOADED action afterwards passing it the fetched flight list. Your store should handle this action and broadcast a change event for all the component observers.
I hope you understand what I'm talking about. If you think I misunderstood your problem, let me know.

Resources