Skip to content

Commit

Permalink
feat(apis): add watchEffect API (vuejs#275)
Browse files Browse the repository at this point in the history
  • Loading branch information
HusamElbashir authored and pikax committed Apr 19, 2020
1 parent 8ff317e commit 983fc96
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 38 deletions.
61 changes: 43 additions & 18 deletions src/apis/watch.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ComponentInstance } from '../component';
import { Ref, isRef } from '../reactivity';
import { assert, logError, noopFn } from '../utils';
import { assert, logError, noopFn, warn } from '../utils';
import { defineComponentInstance } from '../helper';
import { getCurrentVM, getCurrentVue } from '../runtimeContext';
import { WatcherPreFlushQueueKey, WatcherPostFlushQueueKey } from '../symbols';
Expand Down Expand Up @@ -54,6 +54,30 @@ function installWatchEnv(vm: any) {
vm.$on('hook:updated', flushPostQueue);
}

function getWatcherOption(options?: Partial<WatcherOption>): WatcherOption {
return {
...{
lazy: false,
deep: false,
flush: 'post',
},
...options,
};
}

function getWatcherVM() {
let vm = getCurrentVM();
if (!vm) {
if (!fallbackVM) {
fallbackVM = defineComponentInstance(getCurrentVue());
}
vm = fallbackVM;
} else if (!hasWatchEnv(vm)) {
installWatchEnv(vm);
}
return vm;
}

function flushQueue(vm: any, key: any) {
const queue = vm[key];
for (let index = 0; index < queue.length; index++) {
Expand Down Expand Up @@ -222,6 +246,15 @@ function createWatcher(
};
}

export function watchEffect(
effect: SimpleEffect,
options?: Omit<Partial<WatcherOption>, 'lazy'>
): StopHandle {
const opts = getWatcherOption(options);
const vm = getWatcherVM();
return createWatcher(vm, effect, null, opts);
}

export function watch<T = any>(
source: SimpleEffect,
options?: Omit<Partial<WatcherOption>, 'lazy'>
Expand All @@ -247,27 +280,19 @@ export function watch(
callback = cb as WatcherCallBack<unknown>;
} else {
// effect watch
if (process.env.NODE_ENV !== 'production') {
warn(
`\`watch(fn, options?)\` signature has been moved to a separate API. ` +
`Use \`watchEffect(fn, options?)\` instead. \`watch\` now only ` +
`supports \`watch(source, cb, options?) signature.`
);
}
options = cb as Partial<WatcherOption>;
callback = null;
}

const opts: WatcherOption = {
...{
lazy: false,
deep: false,
flush: 'post',
},
...options,
};
let vm = getCurrentVM();
if (!vm) {
if (!fallbackVM) {
fallbackVM = defineComponentInstance(getCurrentVue());
}
vm = fallbackVM;
} else if (!hasWatchEnv(vm)) {
installWatchEnv(vm);
}
const opts = getWatcherOption(options);
const vm = getWatcherVM();

return createWatcher(vm, source, callback, opts);
}
34 changes: 17 additions & 17 deletions test/apis/watch.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const Vue = require('vue/dist/vue.common.js');
const { ref, reactive, watch } = require('../../src');
const { ref, reactive, watch, watchEffect } = require('../../src');

describe('api/watch', () => {
const anyFn = expect.any(Function);
Expand Down Expand Up @@ -298,11 +298,11 @@ describe('api/watch', () => {
const x = ref(0);

// prettier-ignore
watch(() => { void x.value; result.push('sync getter'); }, { flush: 'sync' });
watchEffect(() => { void x.value; result.push('sync effect'); }, { flush: 'sync' });
// prettier-ignore
watch(() => { void x.value; result.push('pre getter'); }, { flush: 'pre' });
watchEffect(() => { void x.value; result.push('pre effect'); }, { flush: 'pre' });
// prettier-ignore
watch(() => { void x.value; result.push('post getter'); }, { flush: 'post' });
watchEffect(() => { void x.value; result.push('post effect'); }, { flush: 'post' });

// prettier-ignore
watch(x, () => { result.push('sync callback') }, { flush: 'sync' })
Expand All @@ -321,24 +321,24 @@ describe('api/watch', () => {
},
template: `<div>{{x}}</div>`,
}).$mount();
expect(result).toEqual(['sync getter', 'sync callback', 'pre callback', 'post callback']);
expect(result).toEqual(['sync effect', 'sync callback', 'pre callback', 'post callback']);
result.length = 0;

waitForUpdate(() => {
expect(result).toEqual(['pre getter', 'post getter']);
expect(result).toEqual(['pre effect', 'post effect']);
result.length = 0;

vm.inc();
})
.then(() => {
expect(result).toEqual([
'before inc',
'sync getter',
'sync effect',
'sync callback',
'after inc',
'pre getter',
'pre effect',
'pre callback',
'post getter',
'post effect',
'post callback',
]);
})
Expand All @@ -352,7 +352,7 @@ describe('api/watch', () => {
const vm = new Vue({
setup() {
const count = ref(0);
watch(_onCleanup => {
watchEffect(_onCleanup => {
onCleanup = _onCleanup;
spy(count.value);
renderedText = vm.$el.textContent;
Expand Down Expand Up @@ -384,7 +384,7 @@ describe('api/watch', () => {
const vm = new Vue({
setup() {
const count = ref(0);
watch(
watchEffect(
() => {
spy(count.value);
},
Expand Down Expand Up @@ -563,7 +563,7 @@ describe('api/watch', () => {

it('simple effect', done => {
const obj = reactive({ a: 1 });
watch(() => spy(obj.a));
watchEffect(() => spy(obj.a));
expect(spy).not.toHaveBeenCalled();
waitForUpdate(() => {
expect(spy).toBeCalledTimes(1);
Expand Down Expand Up @@ -596,10 +596,10 @@ describe('api/watch', () => {
return p;
}

it('work with (single getter)', done => {
it('work with effect', done => {
const id = ref(1);
const promises = [];
watch(onCleanup => {
watchEffect(onCleanup => {
const val = getAsyncValue(id.value);
promises.push(val);
onCleanup(() => {
Expand All @@ -617,10 +617,10 @@ describe('api/watch', () => {
.then(done);
});

it('run cleanup when watch stops (single getter)', done => {
it('run cleanup when watch stops (effect)', done => {
const spy = jest.fn();
const cleanup = jest.fn();
const stop = watch(onCleanup => {
const stop = watchEffect(onCleanup => {
spy();
onCleanup(cleanup);
});
Expand Down Expand Up @@ -651,7 +651,7 @@ describe('api/watch', () => {
it('should not collect reactive in onCleanup', done => {
const ref1 = ref(1);
const ref2 = ref(1);
watch(onCleanup => {
watchEffect(onCleanup => {
spy(ref1.value);
onCleanup(() => {
ref2.value = ref2.value + 1;
Expand Down
6 changes: 3 additions & 3 deletions test/templateRefs.spec.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
const Vue = require('vue/dist/vue.common.js');
const { ref, watch, createElement: h } = require('../src');
const { ref, watchEffect, createElement: h } = require('../src');

describe('ref', () => {
it('should work', done => {
let dummy;
const vm = new Vue({
setup() {
const ref1 = ref(null);
watch(() => {
watchEffect(() => {
dummy = ref1.value;
});

Expand Down Expand Up @@ -35,7 +35,7 @@ describe('ref', () => {
setup() {
const ref1 = ref(null);
const ref2 = ref(null);
watch(() => {
watchEffect(() => {
dummy1 = ref1.value;
dummy2 = ref2.value;
});
Expand Down

0 comments on commit 983fc96

Please sign in to comment.