Redux-devtools-extension: Redux Devtools Extension doesn't reflect the latest state

Created on 4 Nov 2016  路  46Comments  路  Source: zalmoxisus/redux-devtools-extension

Just updated to 2.9.1.0 from 2.8.5.x (I think) and there appears to be an issue with reflecting the current state. This was not an issue in 2.8.5.x.

For example, an action updates the state, however, the diff and state panes on Redux Devtools extension do not show the state being updated, despite seeing the result of the state being reflected in the app. Pressing the "Pause" button within Devtools seems to resync the state and shows it correctly. Sometimes this actually changes the diffs from previous actions, as demonstrated in video2.

This may (probably) be a Devtools bug, but I haven't integrated it into the app to test.

https://dl.dropboxusercontent.com/u/8812332/redux-devtools-bug.mov
https://dl.dropboxusercontent.com/u/8812332/redux-devtools-bug2.mov

bug cannot-reproduce help wanted

Most helpful comment

@agreco,

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
 window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ latency: 0 }) : compose;

If you could provide a repo or an example to reproduce the problem, it would help.

All 46 comments

@lukebarton, yes, it seems related to the introduced latency parameter and the timeouts in 2.9.0. Could you please provide an example and steps to reproduce so I could figure it out? Maybe to modify our demos to replicate that (the source is here).

Also, try to set latency parameter to 0 to see if it helps:

const store = createStore(rootReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__({
  latency: 0
}));

@zalmoxisus setting the latency to 0 fixes the issue for me. I was seeing the same thing. I feel like 0 should probably be the default value for latency.

@kanzelm3, it was. We want to guarantee that the extension is performant out of the box, so it will not freeze highly updated apps like in the demo. Usually, we cannot percept more than an action in 500 ms (which is by default), so no need to waste the cpu.

However, missing the current state due to timeout is a bug we should address. Just cannot reproduce it :-/

Setting latency to 0 isn't fixing it for me.

I'm using redux-saga and these actions are dispatched in forkd sagas. If I do them sequentially with call, Devtools plays fine. But if I async fork them Devtools does not.

Initial
action A
action B -> Async
action C -> Async

When B resolves, it seems to forget the changes caused by action A and thinks that action B reverted whatever action A did, like it cached the state or something. It also doesn't actually reflect the true changes by action B in the diff.

Until I press that pause button :)

@lukebarton could you please modify our example with Redux Saga to replicate that or share another example I could run to reproduce that?

@lukebarton, I modified the example as following and cannot reproduce the issue:

import { takeEvery } from 'redux-saga'
import { take, put, call, fork, select } from 'redux-saga/effects'
import { delay } from 'redux-saga'

export function* incrementAsync() {
  yield call(delay, 0)
  yield put({type: 'INCREMENT'})
}


function* watchDecrement() {
  while(true) {
    yield take('DECREMENT')
    yield fork(incrementAsync)
    yield fork(incrementAsync)
  }
}

export default function* rootSaga() {
  yield [
    fork(watchDecrement)
  ]
}

I'm dispatching DECREMENT and see -1 > 0 > 1. Am I missing anything?

I have the same problem, latency:0 isn't fixing
In my case:
When app load first - store data is empty, but in action payload data is not empty, and in DIFF tab - states are equal (but really store is updated in app, and states are not equal).
But, when I click on pause/start recording button - data appears in a store.
Video:
http://sendvid.com/3f82tyzg
https://www.dropbox.com/s/dhwyhpd67ypcva0/bug.mp4?dl=0

@valdemars, thanks for the details. Unfortunately, video doesn't help much. You can help us by providing a repo to replicate the issue. If you cannot share your project, there are examples you can change in order to reproduce this behaviour.

To clarify, when you click Pause or dispatch any other monitor action, the whole history is sent to the extension, that's why there's no problem in that case. However, in other cases we're sending only new changes to the extension (for performance reason).

Normally, the state is changed before our store.subscribe is invoked, so such behaviour as in those videos shouldn't happen, but something goes wrong, and, having a repro case, it would be easy to figure it out.

In case you want to fix it yourself, it's just this script to investigate.

@zalmoxisus it seems like on my chrome this issue is fixed now. All the correct DIFFs in state are being shown. I am still scratching my head trying to understand what happened (i checked that you didn't release any new version)

@somghosh in fact there were 2.9.1 and 2.9.2 published these days, just didn't add a release tag here as they are mostly blind patches :-/

@zalmoxisus I think I do have the same problem and I can help you to reproduce :

git clone https://gitlab.com/victornoel/petals-cockpit.git
cd petals-cockpit/frontend
npm i
./node_modules/angular-cli/bin/ng serve

Open your browser at : http://localhost:4200/cockpit/workspaces

Now if we look at the state, we have something like :
image

Then click on the + button :
image

and add for example "test"

The state chart is the same :
image

BUT if you click on "log monitor" or "inspector" and then come back on "chart" it's updated to :
image

(notice the 3 minimal workspaces on the right instead of 2)

@maxime1992, thanks for the example.

However, the current issue is Redux specific, while you're using the extension API directly via nrgx store devtools.

The issue you're having is related to Chart Monitor only, the same error as in https://github.com/romseguy/redux-devtools-chart-monitor/issues/11. I'll look into it.

Woops I misunderstood. Thanks for pointing the other issue and for looking into it.

@maxime1992 it should work in 2.10.0, here are more details why that was happening. Thanks again for the example!

@lukebarton, @valdemars could you please check if you're still experiencing the issue in 2.10.0? If yes, please amend my example to reproduce that.

@zalmoxisus it's fixed, thanks a lot ! =)

Hey, just stumbled upon this..

How do I add the latency param via const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;.

Thanks in advance.

@agreco,

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
 window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ latency: 0 }) : compose;

If you could provide a repo or an example to reproduce the problem, it would help.

@zalmoxisus thanks! I was using the first approach and then spotted the __COMPOSE__ method 馃憤

@agreco did that help? I'm still unable to replicate this issue. Maybe you could provide more details?

@zalmoxisus Oh sorry, there was no issue :) I can confirm that your suggestion works!

It was me just reimplementing the window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() to use window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__.

This was the first place I stumbled upon before reading the docs.

Closing this.

@lukebarton, @valdemars if you're still experiencing this issue with the latest please let me know.

@zalmoxisus issue is still there in 2.10.1.1. I'm unable to reproduce it on a smaller project. I'll keep trying.

Also sometimes the slider is not at the end when I open it. Is that related to this? Nothing happens when I drag it till the end. If I click pause, state is updated and slider jumps to the end.

screen shot 2016-11-16 at 3 56 02 pm

@arjunu, it could be related. Are you using any filter parameters?

Could you please also open the console of the window (CMD+Alt+I) to see if there're any exceptions?

@zalmoxisus There are no exceptions. What do you mean by filter parameters? Is it the search box in Inspector?

@arjunu I mean that you can filter actions whether specifying them on the extension setting page or by using actionsWhitelist / actionsBlacklist parameters. In other words are you specifying any parameters?

@zalmoxisus Nope.

@arjunu I'm still stuck in reproducing that. Do you also use redux-saga? Does that happen when starting the app (like in @valdemars' video)? The slider's position isn't accurate everytime the state is altered? Maybe you can find something in my example we can change to correspond to the behaviour of your app?

@zalmoxisus Yes I use redux-saga. When my app loads a bunch of sync actions and async are dispatched. I use code splitting and async reducer injection. Until my chunk loads (I'm dispatching actions to show/hide loader for loading chunks as they're huge) the state is up to date in the inspector. Post that it's not updated. The diff section either shows "states are equal" or a previous action's diff.

I continue to use the app and somewhere down the line for an action (synchronous) the state is up to date. Or if I click Pause. This happens every 9 of 10 times.

The slider thing happens every 1 of 5 times. I tried to replicate the issue on a smaller project with the same setup as mine (with sagas, routes and code splitting). No luck. This issue wasn't there in version 2.8.x.

I used the export option to see what it's like before and after the state is corrected. But I'm seeing:

{
    "payload": {}, "previousState": {}
}

Didn't it used to be something like:

{
    "actionsById": {},
    "computedState": {}
}

Yes, we've changed the format of the exported file in the latest. Before it included a lot of unnecessary data. Anyway, it wouldn't help to debug as the data to export are requested from the client side again.

To inspect the extension's state:

  1. Open chrome://extensions/, check Developer mode on the top of the page, find the extension and click background page link.
  2. In the console you can call store.getState().instances.states.
  3. Don't pay attention to the default key of that object, go to the other one with autogenerated key. The object inside should be equivalent to your application's lifted state (generated by redux devtools enhancer).

Signs that something is wrong: stagedActionIds length is not the same as computedStates length and as actionsById keys length. The state history is in computedStates array.

Also inside your app you could expose the store globally and check if store.liftedStore.getState() is the same as that object.

@zalmoxisus
you're correct

screen shot 2016-11-17 at 12 46 27 am

@arjunu did you dispatched 13 actions or 18? To understand whether the actions or sates were sent wrong.

@zalmoxisus 14. Once I click pause both arrays become 15 in length.

Well, I added a tweak to prevent sending the same state history multiple times, though I still don't know why it is happening. Published 2.10.1.2 which should appear on Chrome Store in about an hour. Please give it a try and let me know whether it helps.

@zalmoxisus Still the same with 2.10.1.2.

@arjunu, could you please give a try to 2.10.1.3 I've just published? If that still doesn't help, then we need an example to reproduce.

@zalmoxisus Sorry, still the same.

@arjunu is the same problem (of having more states than actions) when latency is set to 0?

Is anyone else experiencing this issue using actionsBlacklist, by any chance?

I noticed missing actions after switching my async over to redux-saga. While trying to figure out what was going on, I also noticed that my actionsBlacklist setting wasn't being respected; quite a few instances of the blacklisted actions were still coming through.

When I removed actionsBlacklist entirely, the missing-action issue seemed to be resolved. (Although it's harder to see the actions I care about now that they're swamped by all the actions I previously blacklisted.)

Trying latency: 0 didn't seem to have any effect on this bug one way or the other.

I'm using 2.10.1.3, FWIW.

@tomxtobin I guess the problem still occur without actionsBlacklist, but maybe not that frequently or isn't so obvious that the states don't correspond.

Most likely it is related to some weird behaviour of our subscribe, as stated in the docs:

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.

If I had a repro case, I'd play with it to find a solution.

Maybe someone could try to change saga async example to replicate this problem as it seems most appropriate to the described use case. I didn't have any luck to see any problems with it.

Just change this file:

export default function configureStore() {
+  const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
  const sagaMiddleware = createSagaMiddleware({sagaMonitor})
  return {
-    ...createStore(rootReducer, applyMiddleware(sagaMiddleware)),
+    ...createStore(rootReducer, composeEnhancers(applyMiddleware(sagaMiddleware))),
    runSaga: sagaMiddleware.run
  }
}

Then try to alter the tasks here.

I did a complete rewrite of that part, and have just published 2.10.2.

@tomxtobin, @arjunu, and others who was experiencing this issue, could you please give it a whirl with and without setting latency?

@zalmoxisus It's working now (without latency)!

To clarify, before we were batching/sending actions via setTimeout, but now via lodash throttle, which sends the leading event in the same tick. Not sure we could get the same problem as before with the trailing event.

If someone is still experiencing this issue please let me know.

It's working for me now, too (even with actionsBlacklist). Thanks!

I am having the same issue. 2 consecutive events (raw)

  1. EVENT
{
  payload: {
    connectionStatus: 0
  },
  type: '[EventClient] CONNECTION STATUS UPDATE'
}
  1. EVENT
{
  payload: {
    connectionStatus: 1
  },
  type: '[EventClient] CONNECTION STATUS UPDATE'
}

The state changes. However the Diff part of ngrx devtools says (states are equal)

Was this page helpful?
0 / 5 - 0 ratings

Related issues

leighghunt picture leighghunt  路  5Comments

born2net picture born2net  路  4Comments

zalmoxisus picture zalmoxisus  路  5Comments

MrSkinny picture MrSkinny  路  4Comments

pooltoymae picture pooltoymae  路  3Comments