-
-
Notifications
You must be signed in to change notification settings - Fork 8.4k
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
fix(reactivity): avoid error during nested watch stop #5806
Conversation
❌ Deploy Preview for vuejs-coverage failed.
|
✅ Deploy Preview for vue-sfc-playground ready!
To edit notification comments on pull requests, go to your Netlify site settings. |
✅ Deploy Preview for vue-next-template-explorer ready!
To edit notification comments on pull requests, go to your Netlify site settings. |
@edison1105 while (this.effects.length) {
this.effects[0].stop()
} |
but, Not every |
You are right, let me have a think other methods. |
I feel this can use const effects = this.effects ? [...this.effects] : []
for (i = 0, l = effects.length; i < l; i++) {
effects[i].stop()
} or const effects = this.effects ? [...this.effects] : []
effects.forEach(e => e.stop()) or (this.effects ? [...this.effects] : []).forEach(e => e.stop()) |
Size ReportBundles
Usages
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall I think this is a good change, but I'm not sure about the test.
I did an experiment where I 'fixed' the problem with the following crude hack:
for (i = 0, l = this.effects.length; i < l; i++) {
if (this.effects[i]) {
this.effects[i].stop()
}
}
I don't think this is the right fix, but the proposed test does still pass with this code. I feel like there should be a test that fails with my dodgy fix.
I still think there's a problem with the testing here. The test confirms that stopping the The current code in this PR is: const effectsToStop = this.effects.slice()
for (i = 0, l = effectsToStop.length; i < l; i++) {
effectsToStop[i].stop()
} I believe this does stop the watchers correctly, but the test doesn't really check that. Consider the code below. All it does is introduce an for (i = 0, l = this.effects.length; i < l; i++) {
// This 'fix' would be incorrect, but it looks plausible
if (this.effects[i]) {
this.effects[i].stop()
}
} The code above looks like a plausible fix, but it's actually flawed. If an earlier effect is removed while the loop is being processed it will cause the next effect to be skipped. Currently we don't have a test to protect against an incorrect implementation like this. The test below would catch that problem. It passes for the change proposed on this PR but fails (correctly) for the incorrect fix I showed above: test('removing a watcher while stopping its effectScope', async () => {
const count = ref(0)
const scope = effectScope()
let watcherCalls = 0
let cleanupCalls = 0
scope.run(() => {
const stop1 = watch(count, () => {
watcherCalls++
})
watch(count, (val, old, onCleanup) => {
watcherCalls++
onCleanup(() => {
cleanupCalls++
stop1()
})
})
watch(count, () => {
watcherCalls++
})
})
expect(watcherCalls).toBe(0)
expect(cleanupCalls).toBe(0)
count.value++
await nextTick()
expect(watcherCalls).toBe(3)
expect(cleanupCalls).toBe(0)
scope.stop()
count.value++
await nextTick()
expect(watcherCalls).toBe(3)
expect(cleanupCalls).toBe(1)
}) |
@skirtles-code |
2193284 avoids the extra array copy and also avoid unnecessary |
…ve scope close vuejs#5783 close vuejs#5806
fix #5783
this because
effects
may be changed when an effect stoped.