Subscribe on changes!

`watchEffect` within `effectScope` is removed when component is unmounted

avatar
Dec 11th 2022

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
}
avatar
Dec 11th 2022

But the watchEffect should be attached to the effectScope like a regular watch The return is intentional to simulate a store

avatar
Dec 11th 2022

@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)
})
avatar
Dec 11th 2022

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?