I have a composition function that is shared between multiple components:
// global shared reactive state
let foo
function useFoo() {
if (!foo) { // lazy initialization
foo = ref()
watch(foo, ...) // <- this is stopped when component that created it is unmounted
// make some http calls etc
}
return foo
}
component1 = {
setup() {
useFoo() // lazily initialize
}
}
component2 = {
setup() {
useFoo() // lazily initialize
}
}
The problem is that watch is stopped when the component that called useFoo first is unmounted.
An option for watch
watch(..., {instanceBound: false})
Or a helper that nulls the current instance:
function withNullInstance(cb: Function) {
const old = getCurrentInstance()
setCurrentInstance(null)
cb()
setCurrentInstance(old)
}
function useFoo() {
if (!foo) {
withNullInstance(initFoo)
}
return foo
}
Or export getCurrentInstance and setCurrentInstance so this can be solved in user space.
I think some mechanism for this would be useful for Vuex as well
So in this case, when do you expect it to be stopped? It seems you will need to manually implement a reference count in order to prevent the watcher from running indefinitely (and potentially lead to memory leaks)
So in this case, when do you expect it to be stopped?
Not stopped at all by Vue. The data is essentially global and lives forever. I could run useFoo from the root component App.vue to achieve the same but:
Watch, watchEffect and computed from vue are wrappers around the lower-level primitives computed and effect exported by the reactivity package.
You can import those to create watches that stop whenever you want.
Converting this into a higher-order function with effect (as proposed above) could potentially solve the problem:
export const createEffectComposable = (ref = ref(null)) => {
const _effect = effect(() => {
// ...
})
const _stop = () => stop(effect)
return () => { ref, stop: _stop }
}
export const useFoo = createEffectComposable()
comp1 = {
setup() {
const { ref, stop } = useFoo();
}
}
comp2 = {
setup() {
const { ref, stop } = useFoo();
}
}
composition-api has a solution for this under name activateCurrentInstance https://github.com/vuejs/composition-api/blob/01524a4e4114cbb9443b6d697acb4cfa39f27700/src/utils/instance.ts#L101-L119 . It is private and only changes current instance in composition-api, but the idea is the same.
are wrappers around the lower-level primitives
watch is 300+ lines "wrapper" which is a little bit too much for this task.
@vmihailenco It does a lot of things.
effect from reactivity is basically watchEffect.
RFC #212 provides a solution for this.
Most helpful comment
I think some mechanism for this would be useful for Vuex as well