Skip to content

Commit 434cd8c

Browse files
committed
fix(watch): component should runCleaup() on unmount
close #293
1 parent 7c8386e commit 434cd8c

File tree

2 files changed

+41
-4
lines changed

2 files changed

+41
-4
lines changed

src/apis/watch.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,16 @@ function createVueWatcher(
139139
return vm._watchers[index];
140140
}
141141

142+
// We have to monkeypatch the teardown function so Vue will run
143+
// runCleanup() when it dears down the watcher on teardown.
144+
function patchWatcherTeardown(watcher: VueWatcher, runCleanup: () => void) {
145+
const _teardown = watcher.teardown;
146+
watcher.teardown = function(...args) {
147+
_teardown.apply(watcher, args);
148+
runCleanup();
149+
};
150+
}
151+
142152
function createWatcher(
143153
vm: ComponentInstance,
144154
source: WatcherSource<unknown> | WatcherSource<unknown>[] | SimpleEffect,
@@ -188,8 +198,13 @@ function createWatcher(
188198
before: runCleanup,
189199
});
190200

201+
patchWatcherTeardown(watcher, runCleanup);
202+
191203
// enable the watcher update
192204
watcher.lazy = false;
205+
if (vm !== fallbackVM) {
206+
vm._watchers.push(watcher);
207+
}
193208

194209
const originGet = watcher.get.bind(watcher);
195210
if (isSync) {
@@ -201,7 +216,6 @@ function createWatcher(
201216

202217
return () => {
203218
watcher.teardown();
204-
runCleanup();
205219
};
206220
}
207221

@@ -240,9 +254,12 @@ function createWatcher(
240254
sync: isSync,
241255
});
242256

257+
// Once again, we have to hack the watcher for proper teardown
258+
const watcher = vm._watchers[vm._watchers.length - 1];
259+
patchWatcherTeardown(watcher, runCleanup);
260+
243261
return () => {
244262
stop();
245-
runCleanup();
246263
};
247264
}
248265

test/apis/watch.spec.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,14 @@ describe('api/watch', () => {
1313
});
1414

1515
it('should work', done => {
16+
const onCleanupSpy = jest.fn();
1617
const vm = new Vue({
1718
setup() {
1819
const a = ref(1);
19-
watch(a, spy);
20+
watch(a, (n, o, _onCleanup) => {
21+
spy(n, o, _onCleanup);
22+
_onCleanup(onCleanupSpy);
23+
});
2024
return {
2125
a,
2226
};
@@ -25,13 +29,21 @@ describe('api/watch', () => {
2529
}).$mount();
2630
expect(spy).toBeCalledTimes(1);
2731
expect(spy).toHaveBeenLastCalledWith(1, undefined, anyFn);
32+
expect(onCleanupSpy).toHaveBeenCalledTimes(0);
2833
vm.a = 2;
2934
vm.a = 3;
3035
expect(spy).toBeCalledTimes(1);
3136
waitForUpdate(() => {
3237
expect(spy).toBeCalledTimes(2);
3338
expect(spy).toHaveBeenLastCalledWith(3, 1, anyFn);
34-
}).then(done);
39+
expect(onCleanupSpy).toHaveBeenCalledTimes(1);
40+
41+
vm.$destroy();
42+
})
43+
.then(() => {
44+
expect(onCleanupSpy).toHaveBeenCalledTimes(2);
45+
})
46+
.then(done);
3547
});
3648

3749
it('basic usage(value wrapper)', done => {
@@ -349,11 +361,13 @@ describe('api/watch', () => {
349361
let renderedText;
350362
it('should work', done => {
351363
let onCleanup;
364+
const onCleanupSpy = jest.fn();
352365
const vm = new Vue({
353366
setup() {
354367
const count = ref(0);
355368
watchEffect(_onCleanup => {
356369
onCleanup = _onCleanup;
370+
_onCleanup(onCleanupSpy);
357371
spy(count.value);
358372
renderedText = vm.$el.textContent;
359373
});
@@ -369,13 +383,19 @@ describe('api/watch', () => {
369383
expect(spy).not.toHaveBeenCalled();
370384
waitForUpdate(() => {
371385
expect(onCleanup).toEqual(anyFn);
386+
expect(onCleanupSpy).toHaveBeenCalledTimes(0);
372387
expect(renderedText).toBe('0');
373388
expect(spy).toHaveBeenLastCalledWith(0);
374389
vm.count++;
375390
})
376391
.then(() => {
377392
expect(renderedText).toBe('1');
378393
expect(spy).toHaveBeenLastCalledWith(1);
394+
expect(onCleanupSpy).toHaveBeenCalledTimes(1);
395+
vm.$destroy();
396+
})
397+
.then(() => {
398+
expect(onCleanupSpy).toHaveBeenCalledTimes(2);
379399
})
380400
.then(done);
381401
});

0 commit comments

Comments
 (0)