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?
Related
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.
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
So in plain react, setState triggers the render method. Now, about Redux, let's say I fire an action, the state returned from the reducer(s) is totally new, so it sounds like every component should get updated, though I know it's not true because when I debug my app I can see it, plus it sounds like awful performance. So my question is, does redux look in each component what state slice it needs? in mapStateToProps? and generally how does Redux decide when to call the render method?
I know Redux has optimizations in it... thanks...
A Redux store offers a subscribe() method. When you use connect(), it generates wrapper components that automatically subscribe to the store when they are created. Each individual connected component is a separate subscriber.
When an action is dispatched, the store runs all the subscription callbacks. The wrapper components then call store.getState(). As a first check, if the root state object is === identical to the previous one, the component will stop there. (This is one reason why direct mutation of state in a reducer is bad.)
The next step is to call the provided mapState function and pass in the current state object. The wrapper component then compares the contents of the object returned from mapState against the contents of the object returned last time, using a shallow equality comparison check. If any of the contents have changed based on that shallow check, then the component knows it needs to re-render the "real" component with the new data. (Again, the "shallow" check is another reason why mutation is bad.)
I have a multi-page user registration form in my react/redux app.
When a user clicks "next page," it dispatches a call to update the user's profile in the local redux store (not the server).
When they click "Submit" on the final page, I currently have a click handler along the lines of:
clickHandler: function(formData) {
dispatch(updateProfileInStore(formData);
dispatch(saveProfileToServer());
}
My question is: can I be 100% certain that the Redux store w/ my user profile will update before the second dispatch is called?
I'm worried that in some cases the profile might be written to my database before the last page of form data is added to it.
It's not guaranteed. What you could do is pass saveProfileToServer the new form data have it update state and send the updated form data to the server.
As per the react documentation
setState() does not immediately mutate this.state but creates a
pending state transition. ... There is no
guarantee of synchronous operation of calls to setState and calls may
be batched for performance gains.
The only way to add a synchronous behaviour here is to either use redux thunk or sagas. A redux thunk returns promise that resolves and in sagas, we may have to watch for success action dispatched by updateProfileInStore which needs to call saveProfileToServer.
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.