Skip to content
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

feat(apis): add watchEffect API #275

Merged
merged 1 commit into from
Mar 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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