-
Notifications
You must be signed in to change notification settings - Fork 50
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
How to unwatch #97
Comments
Hey @justin-schroeder ! Had some time today so played around with ways to add in a cleanup reference for I believe this edit to the reactive.ts file would allow cleaning up a watch function as needed:
/**
* Watch a function and track any reactive dependencies on it, re-calling it if
* those dependencies are changed. Returns a function to unwatch.
* @param {CallableFunction} fn
* @param {CallableFunction} after?
* @returns [unknown, () => void]
*/
export function w<
T extends (...args: any[]) => unknown,
F extends (...args: any[]) => any | undefined
>(fn: T, after?: F): [F extends undefined ? ReturnType<T> : ReturnType<F>, () => void] {
const trackingId = Symbol()
// Ensure a new dependency tracker for this watch instance
if (!dependencyCollector.has(trackingId)) {
dependencyCollector.set(trackingId, new Map())
}
let currentDeps: Map<ReactiveProxy<DataSource>, Set<DataSourceKey>> = new Map()
const queuedCallFn = queue(callFn)
function callFn() {
// Reset dependencies for this call
dependencyCollector.set(trackingId, new Map())
const value: unknown = fn()
const newDeps = dependencyCollector.get(trackingId) as Map<ReactiveProxy<DataSource>, Set<DataSourceKey>>
// Clean up after getting the new dependencies
dependencyCollector.delete(trackingId)
// Remove old observers
currentDeps.forEach((propertiesToUnobserve, proxy) => {
propertiesToUnobserve.forEach((prop) => proxy.$off(prop, queuedCallFn))
})
// Add new observers
newDeps.forEach((properties, proxy) => {
properties.forEach((prop) => proxy.$on(prop, queuedCallFn))
})
// Update the current dependencies
currentDeps = newDeps
return after ? after(value) : value
}
// Setup initial call and observers
const result = callFn()
// Setup unwatchfunction
function unwatch() {
// Remove all observers set by this watch
currentDeps.forEach((propertiesToUnobserve, proxy) => {
propertiesToUnobserve.forEach((prop) => proxy.$off(prop, queuedCallFn))
})
// Optional: If the watched function itself is reactive, remove this callback
if (isReactiveFunction(fn)) fn.$off(callFn)
}
// If the function is reactive, setup to re-run on reactive function changes
if (isReactiveFunction(fn)) fn.$on(callFn)
// Return the result of the initial function call and the cleanup function
return [result, unwatch];
} Basically it would then return both the initial call result and a reference to the clean up function. Example usage: let unwatch;
onMount(() => {
// Setup the watch
const [watchResult, unwatch] = w(someReactiveFunction);
// Do something with watchResult if needed
});
onUnmount(() => {
// Clean up when the component unmounts
unwatch();
}); |
I agree, this seems like it would do the ticket. It is a significant breaking change too however — which will probably be required anyway whenever I get around to implementing something like this. There are a number of refactors that have been worked on that havent yet seen the light of day, but as soon as I get more time to tackle this unwatch will be a high priority! |
Hey!
Really loving ArrowJS - super easy to work with :)
One issue I have found is no way to "un watch". Take for example:
watch
on some reactive variable.watch
keeps running.It would be really helpful if the
watch
returned some sort of reference which we could then remove on unmount.A common time this happens is with multi-page apps. When spanning between pages, elements are constantly getting added and removed from the dom. After a bit you just have a bunch of old watch's sitting out there.
Any suggestions are appreciated!
Thanks!
The text was updated successfully, but these errors were encountered: