Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Devtools seems to cause issues with actions dispatching additional times #229

Closed
karptonite opened this issue Aug 2, 2017 · 16 comments
Closed

Comments

@karptonite
Copy link
Contributor

karptonite commented Aug 2, 2017

I'm submitting a...


[ ] Regression (a behavior that used to work and stopped working in a new release)
[X] Bug report  
[ ] Feature request
[ ] Documentation issue or request
[ ] Support request

What is the current behavior?

I'm running into an issue with the devtools where sometimes (but not always), an action might be dispatched once, but hit the corresponding reducer many (8-10) times.
No minimal implementation, but I can say that it happens fairly often (at least 1 time in 3). By putting debugging into the ngrx store code, I've confirmed that these actions aren't coming into the stores's dispatch or next methods, nor are they showing up in the devtools lists of actions. But this is having real effects--not only are the reducer functions run, but they are causing changes to the store that then trigger additional (real) actions (because of subscriptions).

Expected behavior:

with devtools off, I don't see this problem. I expect that devtools will not cause additional actions to be dispatched.

Minimal reproduction of the problem with instructions:

This I can't provide. I can't even guarantee it will happen every time in my application, which has quite a few moving parts. I do, however, have a "working" example of the issue locally, and am more than happy to work with anyone who has a better understanding of devtools to try to figure this out. Find me on Gitter!

Version of affected browser(s),operating system(s), npm, node and ngrx:

ngrx 4.02. , Chrome 59 with the latest redux-devtools. MacOS 10.12.2.

@karptonite karptonite changed the title Devtools seems to cause issues with actions Devtools seems to cause issues with actions dispatching extra times Aug 2, 2017
@karptonite karptonite changed the title Devtools seems to cause issues with actions dispatching extra times Devtools seems to cause issues with actions dispatching additional times Aug 2, 2017
@karptonite
Copy link
Contributor Author

On further investigation, the number of times the action is dispatched "extra" appears to be related to the number of times the StoreModule.forFeature() is called. the more features added, the more extra actions.

@karptonite
Copy link
Contributor Author

karptonite commented Aug 3, 2017

OK, this is a bit of conjecture, but it is my best guess as to what might be going on.

Somehow, when devtools is running, and an additional feature is added with StoreModule.forFeature(), all previous actions are replayed on the state, with the new reducers in place. The final state is the same. But in our case, we have two actions that run one after another: initializeSearch and setSearchType, which, when run one after another, effectively remove an object then recreate it with the same values as before; but it is no longer the identical object, as far as distinctUntilChanged is concerned, so a value is emitted from a selector, and that fires off another action for us.

Does this sound like something that could be going on? If so, it seems like it is an issue with devtools that we need to address.

[edit] I can confirm that the behavior is consistent with what I described above. If I go to one route, then navigate to another route that lazy loads a feature for the store, I see these phantom actions.

Devtools and the real store are using the same store, which would be fine if devtools didn't run actions on the store in addition to those that are dispatched. You can't be assured that rerunning all the actions on the store will give you an identical state if you are comparing objects by reference. If devtools could add a feature to a state without replaying past actions (assuming that is what it is doing), that would fix this problem.

@Problematic
Copy link

Adding my two cents here... we recently saw that this can cause environment-specific bugs, where a feature works in dev but breaks in prod. As a concrete example, consider an effect that uses startWith(new SomeAction()) to dispatch immediately on app boot, but the EffectsModule is listed before the feature module that registers the feature reducer.

Leaving aside the fact that order-of-operations bugs such as this one are easy to produce and can be difficult to diagnose, this (correctly) fails in production because the action that the effect dispatches is not handled by the yet-to-be-registered feature reducer. In development, everything appears to work as intended because previously-dispatched actions are dispatched again after the feature reducer is registered, so the store is updated.

This is not a compile-time error, and it's not even technically a runtime error, but it is incorrect and potentially app-breaking behavior that differs between prod and dev. I would consider the store devtools to be dangerously unusable until this is resolved. Is there an estimated timeline for a fix?

@phillipzada
Copy link
Contributor

@karptonite can you provide some code and steps to reproduce this issue please.

@Problematic
Copy link

@phillipzada here's a repro: https://plnkr.co/edit/g0tIhEdeOaVvCWh3ihDd?p=preview

You can see the actions logging multiple times in the console (you'll probably want to turn off logging of XMLHttpRequests since plunkr is pretty spammy) when you click through to my-feature (which is lazy-loaded to make it easier to see the re-firing of actions). If you change development to false in src/app.ts on line 31, you'll see that it works as expected (and also demonstrates the difference in prod vs dev behavior with the displayed "hello" message on /my-feature).

@phillipzada
Copy link
Contributor

@Problematic I had a play around with your sample code (thanks btw), and set the maxAge to 3 for example and it confirms @karptonite comments about the replay functionality.

Now to try and figure out the solution... 😫 My original thoughts was having 2 implementations of devtools, one with a historic view with repeats and another without which is based off the older (v3) implementation.

Can anyone confirm if effects are being fired twice due to the replay, which can result in API calls in some implementations or is the replay just running through the motions?

@karptonite
Copy link
Contributor Author

karptonite commented Oct 26, 2017

@phillipzada I can confirm that I have effects being fired twice. To be clear, these are effects that are intiated by values that are selected from the store. My previous example still stands:

... in our case, we have two actions that run one after another: initializeSearch and setSearchType, which, when run one after another, effectively remove an object then recreate it with the same values as before; but it is no longer the identical object, as far as distinctUntilChanged is concerned, so a value is emitted from a selector, and that fires off another action for us.

That action that is fired off triggers an effect (one which doesn't do much in this case, but easily could).

As for a solution, this might be messy, and I know nothing of the internal implementation, but here is what I would do: When new features are added, run the replay through the reduces as before (without emitting any intermediate values), then deeply compare the values of the new state with the old; then build a new composite state, as much as possible using the actual objects from the old state, when the values match.

Does this sound like something that might work?

@phillipzada
Copy link
Contributor

@karptonite I'm trying to schedule some time to up into the source and see if there is a way to circumvent the issue. In relation to your solution i'm not sure it will cause a side effect with selectors & withLatestFrom operator, would need to see a sample.

@TerryHibbert
Copy link

Hey @MikeRyanDev, I'm still getting double triggering in version 5.2 when StoreDevtools has been added. Should this be reopened?

My app works fine without StoreDevtools. When added, a single action dispatch double triggers an effect resulting in a double API call. The superfluous API call results in sadness.

@hienpham2tiki
Copy link

hienpham2tiki commented Apr 12, 2018

Hi @TerryHibbert
I dived into the devtools source code and I saw this line https://github.com/ngrx/platform/blob/v5.2.0/modules/store-devtools/src/devtools.ts#L96 cause this issue.
Actually, after run the action, store devtools emit this action to the ScannedActionsSubject, while the State of @ngrx/store emit another time: https://github.com/ngrx/platform/blob/v5.2.0/modules/store/src/state.ts#L52.
--Updated --
Delete line 96 of devtools.ts resolve this issue!

@hienpham2tiki
Copy link

Just a hot fix:
When the devtools is used, add this line to the root provider:

import { State } from '@ngrx/store';

{
    providers: [{ provide: State, useValue: null }],
}

@haskelcurry
Copy link

@hienduyph Didn't work for me :( My project uses version 4.1.1 though

@haskelcurry
Copy link

Is it fixed already in the newer versions? Please help me to clarify this, thank you

@haskelcurry
Copy link

Hi! Why is it closed? Is it fixed?

@adornala
Copy link

@hienduyph Didn't work for me :( My project uses version 4.1.1 though

Me too. It didn't work.

@xellafe
Copy link

xellafe commented Jan 8, 2024

Just a hot fix: When the devtools is used, add this line to the root provider:

import { State } from '@ngrx/store';

{
    providers: [{ provide: State, useValue: null }],
}

For version 17 this seems to work

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants