core
core copied to clipboard
`watchEffect` within `effectScope` is removed when component is unmounted
Vue version
edge
Link to minimal reproduction
https://sfc.vuejs.org/#eNqFVE+vmzAM/yoel1KJkntHq03brrtUu3GhqWl5LyRREto39fHd5yTAo1R6ExJ2Yvvnf7HvyXet82uHyTYpHLZaVA73pQQoTs0VuKis3ZUJvyB/LZMgIFEjdefA/dU4yo7qrUzgumnVCQVd2ou60UVz+uDZaC2qIwqolZlk+wMRcBeEghPA/uCUwT8WTcHCuWDBJsbFKDDiAj8pkuumfvRVsFk+dLTcNNqBRddpEJU8k7qzIamm1co4uIPBGnqojWphRUVZfZ1kH56iNGfh5ie2ypfPa5aSK2nJg09m57FSZzpck6hg0XuIJMmSCLppK52/WCWp+HefTzkIKKothBt/R/D+XCYX57TdMmZr7n2+2FyZMyMuN510TYs52nZzNOpGcRJwmWQzDEaXVzQbg/KEBs1nmAvVJ1wP25eyp1SsL0PuLOUwL2MGt8rxy0B+1TVylwEGeuBK47LO/hNI1fNCOgfeYxOPbwG47iR3jZLQWfzRkbANPUjXsVZNDWmwWFMArjNysoeISk2J9P19HsnUJVLzBqOar2pK4Lv92IvY31pU56G/dSXsYEpVDv9Zvo/G0VwJzIU6p6uZXkBcZYHk10pQNNGkH+gMOfVKGfwX+VPMyA1FSieUAD1qhaQC8cy8oNT38IgfBsDvj2HCZrMVp20+YdS6YJctmjg9h5yNL2o+U/cYHEg800CfSHv39AqWc/a8z8YNdOyco2f0jYuGv1KoQ0e/eDptOYDfwdmwk7xsWEcDCosw41GbUeJVaX5jzNBTtaL+qDGusMWGSvp/xP3dXw==
Steps to reproduce
- Click the "negate button" -> see multiple logs
- untick the checkbox and tick it again (triggers unmount + mount of component)
- Click the negate button again -> see only one log
What is expected?
watchEffect should trigger because it's inside of a detached effectScope
What is actually happening?
watchEffect isn't run anymore after the wrapping component unmounts
System Info
na
Any additional comments?
From https://github.com/vuejs/pinia/issues/1862
https://github.com/vuejs/core/blob/11bd8db768f1f3587ed7e820357369551c081c60/packages/runtime-core/src/apiWatch.ts#L232-L236
let scope;
let store;
export function useCustomStore() {
- if (store) return store; // comment this line will works fine.
scope = scope || effectScope(true);
store = scope.run(() => {
const flag = ref(false);
+ The root cause is that an unmounted instance is cached in `watchEffect`.
+ When the component is re-mount, we create a new instance of the component.
+ But because `if (store) return store;` causes `watchEffect` not to be re-executed,
+ so its internal instance is the component instance that has been unmounted.
watchEffect(() => {
console.log('watchEffect flag', flag.value)
})
watch(flag, () => {
console.log('watch flag', flag.value)
})
return ({
flag,
});
});
return store
}
But the watchEffect should be attached to the effectScope like a regular watch The return is intentional to simulate a store
@posva Actuality, the instance in the regular watch callback is also incorrect and is unmounted. It can execute because we don't determine if the instance is unmounted like watchEffect does.
watch(flag, () => {
console.log('watch flag', flag.value)
})
I think watch(Effect)() should care about the currentInstance only if the activeEffectScope === instance.scope during the watcher's creation. But currently, the getter created for a watchEffect() will return early if the instance it was created "in" has unmounted.
Would you agree?