Handling multiple async calls with flux - reactjs

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.

Related

Simultaneous async API calls in react hooks

I've been using hooks in my React project for a while now and have developed a hook for getting data, similar to many others, which I use like this.
const [callApi, isLoading, result, errrors] = useData();
callApi is a function which you call with your data and axios config, it makes the API call and updates the other variables. I deal with the result in a useEffect() call which sets the state when it receives new data. It's fairly simple.
The problem is that while it works very well for one request at a time, it has a single isLoading state and a single result state, which is no good if it's called before the previous call is finished. Let's say my user sending a message to his friends and the UI shows a list of friends with a "send message" button next to each name, they might click 3 buttons and make 3 calls in quick succession. We then have a race condition to see which one sets the result first.
I'm used to imperative programming with promises, in my old Angular code the button click would call an event handler which would call the api and the promise .then() would then deal with the result. This could happen 100 times simultaneously and each one would resolve seperately and then update the UI.
So my question is, is there an idiomatic way of doing this declaratively using react hooks so that I can fire off multiple requests and deal with the loading/result/errors separately?

React-Redux: Mutating the state to emulate action side-effects with redux-undo

I have a few fields that, when updated, change the href of an image (which is really an endpoint of an external API that generates a PNG based on text input).
When that image loads, it triggers an action to update another component to match the image size (that it can only get after the image has finished loading). I set redux-undo to exclude that action from the history since it's not a user action.
Here's my problem: When I do an action that does NOT trigger a change, then undo that action, the state is wrong since the size has changed in between user actions.
An idea I had was to mutate the state (yeesh):
In reducer
case 'UPDATE_TARGET_SIZE':
/**
* HERE BE DRAGONS
* I am also mutating the current state since this action needs to
* NOT count as an user action
*/
state.targetWidth = action.targetWidth
state.targetHeight = action.targetHeight
return {
...state
}
While it works with no apparent drawbacks... it feels like a dirty hack. Is there another way of doing so or is it safe as long as I know why I'm mutating the state?
Can a lib like redux-saga help me? I admit I have not read its documentation since I am not making "real" API calls and I don't want to have an overkill solution.
Edit :
Here's a better example:
I type in a text input, which causes the href to change. A new image is loaded, which triggers the action that is excluded from the history. At that point, the history does not have the modification of that last action.
If I do an action that copies that part of the sate (in the right reducer), the state is fine. If I do an action that touches only another reducer, it will still be wrong.
So when I press undo, I undo to the wrong state. But if I get that action to mutate the state, then it's always valid.
Can a lib like redux-saga help me? I admit I have not read its
documentation since I am not making "real" API calls and I don't want
to have an overkill solution.
Redux-saga allows to perform custom side effects and introduce process manager since it has own event loop. Applying to original task, saga can help with splitting action with request and response in time.
Typical use case is emitting action with postfix _REQUEST, like LOAD_IMAGE_REQUEST, which intercepts with saga manager and does not pass into reducers. Then after async operation is done, saga emits _SUCCESS or _FAILURE-like action, dependent on sucessfullity of operation. That actions passed into store and applied with reducers.
Also, there is ideology called optimistic update. In that case _REQUEST event passed into store, but if action is failed, saga sents compensating event, which rollback optimistic action

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

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

Call action after callback from listener and... cannot dispatch in the middle of dispatch?

I'm in trouble with React and Flux... We have an application that is pretty similar to the new Flux chat example. We have the famous error "cannot dispatch in the middle of dispatch". But, it's hard to us to think in a good way to resolve this problem in some cases.
Our doubt is identical to this: https://groups.google.com/forum/#!topic/reactjs/mVbO3H1rICw, but I can't understand very well the solution adopted. As far as I understand, is not a very elegant solution.
Here is the sequence of events:
Action A is dispatched;
The Store updates it's internal state and emits the change message;
A react component X receives the change message (by the callback of the listener) and updates it's state (setState);
The component X renders and as part of that a new component Y is mounted too. We choose the component (Y, Z, etc...) to be rendered using the information of the state;
The new component Y needs data to display that isn't initially loaded. So we call a API in the componentDidMount() of the component Y, that calls an action B.
Then, with the new dispatcher in the Action B, we have this dispatch error.
If you consider that our application logic have some issue, I can bring some practicals examples to show why this scenario is common for us. Any idea of how refactor this "flux" is very welcome.
Thanks for any help!
i think you need to use the waitFor token from the dispatcher before launching action b (youre using the dispatcher from the flux npm module right?). additionally, what can you do is make the ajax call from your store during action A if youre always going to need that data from action b.
solution b would look like this in /* store.js */
Dispatcher.register(function(payload){
switch payload.type {
case ACTION_A:
/* do your state logic */
StoreDataAccessLayer.apiCall()
}
}
where you have a Data Access Layer class / object that wraps your ajax call to the api. using the state from your store as inputs, and calling trigger change from the success function. this is the primitive solution i've seen when using the flux npm module if youre not going with waitFor

Fluxxor - how to dispatch an action in response to another?

I feel like subtitling this question "Does Fluxxor want my application to work?". No doubt however I have not grasped how to co-operate with Fluxxor...
The error is:
Cannot dispatch an action ('NEXT_AJAX_REQUEST_IS_ABOUT_TO_BE_MADE') while another action ('FIRST_AJAX_REQUEST_IS_RETURNED') is being dispatched
Component A is the owner of Component B
Component A calls an action creator in its componentWillReceiveProps. This action creator makes an AJAX request. If successful we dispatch FIRST_AJAX_REQUEST_IS_RETURNED with some data into a store. The store updates. It emits change. Component A is listening for this change. Component A updates its state. Component A re-renders passing this state as props to Component B. Component B in its componentWillReceiveProps calls an action creator to make a second AJAX request based on the data it now has. Before the actual AJAX request is made the action creator dispatches NEXT_AJAX_REQUEST_IS_ABOUT_TO_BE_MADE - in order to put a store into a certain state.
I get the message about dispatching one action while another is in progress it seems because Fluxxor does not consider the first action complete - even though I cannot have made the second until the AJAX request was completed and I have the data. The issue seems to be calling the second action from the componentWillReceiveProps method of a child component in response to changes in the owner component's state arising from the first action.
So. I tried moving the second action to the success 'callback' in the first store. I still get the same message. Even if this was the right thing to do...
The second action depends on data contained in the first action. I cannot merge these two into one single request/action and then update 2 stores using waitFor.
http://fluxxor.com/guides/react.html#batched-updates
This fixed it.
However I do not understand why and would really appreciate an explanation if anyone has it.
I think that the effect is that Component A will not re-render until state changes arising from getStateFromFlux have fully completed. So that when Component B calls its action creator we are now fully done with the first action.
Is this approximately correct?

Resources