I'm working on an app, and there is a point whereby 3 or 4 actions are dispatched fairly quickly, and I'm not seeing them appear in the devtools. I've verified every which way that they are actually being dispatched into the store, but they're not showing up . Have there been any reports of actions being "dropped" this way?
Thanks for the report. I'm also dispatching multiple actions simultaneously in my apps and have no problems with that.
If there are lots of actions and only the first of them are missing, you should increase Limit the action history to on the Settings (Options) page.
If that's not the problem please provide a minimal repo or jsfiddle to reproduce the issue.
Not so many that it hits the limit. There are four dispatched basically simultaneously and it doesn't seem to be appearing in the devtools. Then there are four more later, of which one or two are showing up. The state appears the be changing in response to the actions though, so I'm not sure where the bug is just yet. If I'm the only one experiencing it, it's probably me, but I'll keep investigating and see if I can repro.
Thanks for your help. Will close when I track it down.
Usually an action doesn't appear in the monitor logs because the state is mutated somewhere. It can be checked by adding adding redux-immutable-state-invariant middleware. Not sure that's the issue.
Also make sure, you don't filter actions (added action types on the Options page or with actionsBlacklist parameter).
Is it possible that, if the payload is not serializable, the action would get missed? The objects attached to the dropped Actions' payload are not simple JSON-y objects (for legacy migration reasons) but complex objects w/ methods and some state and all that noise.
If you don't see any exceptions in the console, then jsan should serialize them. The problem however is that it can take a lot of time to serialize them, probably that causes the issue.
On the off-chance jsan chokes on an action, e.g. throws an error or something, would that show up visibly or possibly drop the action silently?
It would be thrown an exception in this case, seen in the console.
Do you have dispatched more than 4 actions in a second? If yes, then this part should be the problem. So, while serializing the history, a new action is dispatched and skipped. If that's the case, I will remove this part as being ineffective.
I removed that part in v2.6.0, which should fix the issue. If not, please reopen it.
Ok cool, thanks. Is there a way to tell what version of the extension I have installed?
Sure, just look for the extension on chrome://extensions/. Chrome autoupdates the extensions, but it can take several hours.
I'm running v2.8.2.2 and am also seeing this.
In my case, I have an action that has store.subscribe listeners firing other actions based on the results of the fired action (should conditions be met). And in those cases, it seems like only the final action gets picked up in the extension/console. However all the state updates are happening, and that final action displayed does show the correct state diff.
Also should point out that if I put a timeout around the action being dispatched in the store.subscribe listener everything shows in the extension. As opposed to it firing as soon as the state updates and the conditions in the listener are met.
@brianfegan, could you please provide an example repo or jsfiddle to reproduce. You could fork our examples.
I'm trying to modify this action to
export function incrementAsync(delay = 1000) {
return dispatch => {
+ dispatch(increment());
+ dispatch(decrement());
setTimeout(() => {
dispatch(increment());
}, delay);
};
}
and all 3 actions get picked as expected.
I'll work on putting together a jsFiddle, but here's some code that more clearly represents what I'm seeing. Its a bit different from what you put together.
There's a login fn that returns a fn (using thunk). That fn makes an async call that returns a promise. When that promise is fulfilled, I dispatch a LOGIN_SUCCESS action.
login(data) {
return dispatch => user.login(data)
.then(resp => dispatch({type:'LOGIN_SUCCESS', resp}));
}
Meanwhile, there's another file to manage a modal that contains the login form. To keep things isolated -- the login actions shouldn't care about the login modal UI -- I'm using store.subscribe in the management of the modal to know if a user has logged in so we can close the modal.
store.subscribe(() => {
let state = store.getState().toJS();
if (state.loginModal.isOpen && state.auth.authorized) {
store.dispatch({type:'CLOSE_LOGIN_MODAL'});
}
});
What happens is LOGIN_SUCCESS gets dispatched, which immediately dispatches CLOSE_LOGIN_MODAL as well (since those conditions are met). Redux picks them both up and all states are updated as expected. However, the dev tools extension only shows CLOSE_LOGIN_MODAL. Yet it shows the state changes that happened in both LOGIN_SUCCESS and CLOSE_LOGIN_MODAL merged together as if they happened in CLOSE_LOGIN_MODAL.
However, if I wrap a setTimeout around the action dispatched in the subscribe listener...
store.subscribe(() => {
let state = store.getState().toJS();
if (state.loginModal.isOpen && state.auth.authorized) {
window.setTimeout(() => store.dispatch({type:'CLOSE_LOGIN_MODAL'}), 0);
}
});
...then the dev tools extension displays both LOGIN_SUCCESS and CLOSE_LOGIN_MODAL.
@brianfegan, thanks for the details, I can reproduce it. It's weird but seems to be as expected:
You may call dispatch() from a change listener, with the following caveats:
...
- The listener should not expect to see all state changes, as the state might have been updated multiple times during a nested dispatch() before the listener is called. It is, however, guaranteed that all subscribers registered before the dispatch() started will be called with the latest state by the time it exits.
In other words the extension's subscribe gets the last change (even though it's invoked 2 times) in case when dispatch is from inside a store.subscribe.
I managed to solve this by moving extension's subscribe from setTimeout, which was added recently. However we need that for iframes. So, the problem will remain just when the app is from inside an iframe.
Another solution would be to count actions, so we'll pick the right one from the history even if the current one is not the last.
I'll think which one to choose, and will publish a patch tomorrow. Most likely, I'll go with the first one for now, as it's a regression from v2.8.2.
Another problem with using store.subscribe like in your case is that it will cause side effects while time travelling. So, when moving back and forth you'll have a lot of new CLOSE_LOGIN_MODAL actions in the history (as the subscribe is invoked and the condition is met). I'd suggest to use a middleware for this as I described here. So, you can lock those changes. In case you still want to dispatch from inside the subscribe and to use timetravelling there's an undocumented window.__REDUX_DEVTOOLS_EXTENSION_LOCKED__ to check before dispatching.
Thanks for the update, @zalmoxisus!
I'll also look into the suggestion you made. (But that CLOSE_LOGIN_MODAL will only fire if the redux store state has the modal as open and our used as authorized / logged in. So if I went backwards in state to a snapshot where the user isn't logged in, it wouldn't fire.)
Do you have any idea where the fix you committed^ will be pushed and available in the extension?
@brianfegan, should work now in 2.8.4. Let me know if there are still any troubles there.
Confirmed as fixed on my end. Thanks, @zalmoxisus!
This is definitely resolved for me with the latest version. I will also mentioned, in reviewing the code, there was a mutation in the reducer, so the same array was being returned, rather than a new one, which (maybe?) caused some issues as well, in case anyone else has this issue.
Most helpful comment
Thanks for the report. I'm also dispatching multiple actions simultaneously in my apps and have no problems with that.
If there are lots of actions and only the first of them are missing, you should increase
Limit the action history toon the Settings (Options) page.If that's not the problem please provide a minimal repo or jsfiddle to reproduce the issue.