diff --git a/packages/runtime-core/__tests__/apiSetupContext.spec.ts b/packages/runtime-core/__tests__/apiSetupContext.spec.ts index bc3c2065..8f7ab078 100644 --- a/packages/runtime-core/__tests__/apiSetupContext.spec.ts +++ b/packages/runtime-core/__tests__/apiSetupContext.spec.ts @@ -6,7 +6,7 @@ import { render, serializeInner, nextTick, - watch, + watchEffect, defineComponent, triggerEvent, TestElement @@ -55,7 +55,7 @@ describe('api: setup context', () => { const Child = defineComponent({ setup(props: { count: number }) { - watch(() => { + watchEffect(() => { dummy = props.count }) return () => h('div', props.count) @@ -88,7 +88,7 @@ describe('api: setup context', () => { }, setup(props) { - watch(() => { + watchEffect(() => { dummy = props.count }) return () => h('div', props.count) diff --git a/packages/runtime-core/__tests__/apiWatch.spec.ts b/packages/runtime-core/__tests__/apiWatch.spec.ts index ef05452f..796b2cbf 100644 --- a/packages/runtime-core/__tests__/apiWatch.spec.ts +++ b/packages/runtime-core/__tests__/apiWatch.spec.ts @@ -1,4 +1,12 @@ -import { watch, reactive, computed, nextTick, ref, h } from '../src/index' +import { + watch, + watchEffect, + reactive, + computed, + nextTick, + ref, + h +} from '../src/index' import { render, nodeOps, serializeInner } from '@vue/runtime-test' import { ITERATE_KEY, @@ -13,10 +21,10 @@ import { mockWarn } from '@vue/shared' describe('api: watch', () => { mockWarn() - it('watch(effect)', async () => { + it('effect', async () => { const state = reactive({ count: 0 }) let dummy - watch(() => { + watchEffect(() => { dummy = state.count }) expect(dummy).toBe(0) @@ -117,10 +125,10 @@ describe('api: watch', () => { expect(dummy).toMatchObject([[2, true], [1, false]]) }) - it('stopping the watcher', async () => { + it('stopping the watcher (effect)', async () => { const state = reactive({ count: 0 }) let dummy - const stop = watch(() => { + const stop = watchEffect(() => { dummy = state.count }) expect(dummy).toBe(0) @@ -132,11 +140,32 @@ describe('api: watch', () => { expect(dummy).toBe(0) }) + it('stopping the watcher (with source)', async () => { + const state = reactive({ count: 0 }) + let dummy + const stop = watch( + () => state.count, + count => { + dummy = count + } + ) + + state.count++ + await nextTick() + expect(dummy).toBe(1) + + stop() + state.count++ + await nextTick() + // should not update + expect(dummy).toBe(1) + }) + it('cleanup registration (effect)', async () => { const state = reactive({ count: 0 }) const cleanup = jest.fn() let dummy - const stop = watch(onCleanup => { + const stop = watchEffect(onCleanup => { onCleanup(cleanup) dummy = state.count }) @@ -187,7 +216,7 @@ describe('api: watch', () => { const Comp = { setup() { - watch(() => { + watchEffect(() => { assertion(count.value) }) return () => count.value @@ -221,7 +250,7 @@ describe('api: watch', () => { const Comp = { setup() { - watch( + watchEffect( () => { assertion(count.value, count2.value) }, @@ -263,7 +292,7 @@ describe('api: watch', () => { const Comp = { setup() { - watch( + watchEffect( () => { assertion(count.value) }, @@ -363,14 +392,14 @@ describe('api: watch', () => { expect(spy).toHaveBeenCalledTimes(3) }) - it('warn immediate option when using effect signature', async () => { + it('warn immediate option when using effect', async () => { const count = ref(0) let dummy - // @ts-ignore - watch( + watchEffect( () => { dummy = count.value }, + // @ts-ignore { immediate: false } ) expect(dummy).toBe(0) @@ -388,7 +417,7 @@ describe('api: watch', () => { events.push(e) }) const obj = reactive({ foo: 1, bar: 2 }) - watch( + watchEffect( () => { dummy = [obj.foo, 'bar' in obj, Object.keys(obj)] }, @@ -423,7 +452,7 @@ describe('api: watch', () => { events.push(e) }) const obj = reactive({ foo: 1 }) - watch( + watchEffect( () => { dummy = obj.foo }, diff --git a/packages/runtime-core/__tests__/components/Suspense.spec.ts b/packages/runtime-core/__tests__/components/Suspense.spec.ts index a530532f..6a115e39 100644 --- a/packages/runtime-core/__tests__/components/Suspense.spec.ts +++ b/packages/runtime-core/__tests__/components/Suspense.spec.ts @@ -9,6 +9,7 @@ import { nextTick, onMounted, watch, + watchEffect, onUnmounted, onErrorCaptured } from '@vue/runtime-test' @@ -163,7 +164,7 @@ describe('Suspense', () => { // extra tick needed for Node 12+ deps.push(p.then(() => Promise.resolve())) - watch(() => { + watchEffect(() => { calls.push('immediate effect') }) @@ -265,7 +266,7 @@ describe('Suspense', () => { const p = new Promise(r => setTimeout(r, 1)) deps.push(p) - watch(() => { + watchEffect(() => { calls.push('immediate effect') }) diff --git a/packages/runtime-core/__tests__/errorHandling.spec.ts b/packages/runtime-core/__tests__/errorHandling.spec.ts index 6396c6b3..f6907949 100644 --- a/packages/runtime-core/__tests__/errorHandling.spec.ts +++ b/packages/runtime-core/__tests__/errorHandling.spec.ts @@ -7,7 +7,8 @@ import { watch, ref, nextTick, - defineComponent + defineComponent, + watchEffect } from '@vue/runtime-test' import { setErrorRecovery } from '../src/errorHandling' import { mockWarn } from '@vue/shared' @@ -241,7 +242,7 @@ describe('error handling', () => { expect(fn).toHaveBeenCalledWith(err, 'ref function') }) - test('in watch (effect)', () => { + test('in effect', () => { const err = new Error('foo') const fn = jest.fn() @@ -257,7 +258,7 @@ describe('error handling', () => { const Child = { setup() { - watch(() => { + watchEffect(() => { throw err }) return () => null @@ -268,7 +269,7 @@ describe('error handling', () => { expect(fn).toHaveBeenCalledWith(err, 'watcher callback') }) - test('in watch (getter)', () => { + test('in watch getter', () => { const err = new Error('foo') const fn = jest.fn() @@ -298,7 +299,7 @@ describe('error handling', () => { expect(fn).toHaveBeenCalledWith(err, 'watcher getter') }) - test('in watch (callback)', async () => { + test('in watch callback', async () => { const err = new Error('foo') const fn = jest.fn() @@ -332,7 +333,7 @@ describe('error handling', () => { expect(fn).toHaveBeenCalledWith(err, 'watcher callback') }) - test('in watch cleanup', async () => { + test('in effect cleanup', async () => { const err = new Error('foo') const count = ref(0) const fn = jest.fn() @@ -349,7 +350,7 @@ describe('error handling', () => { const Child = { setup() { - watch(onCleanup => { + watchEffect(onCleanup => { count.value onCleanup(() => { throw err diff --git a/packages/runtime-core/src/apiWatch.ts b/packages/runtime-core/src/apiWatch.ts index da3df303..1a2ecb5e 100644 --- a/packages/runtime-core/src/apiWatch.ts +++ b/packages/runtime-core/src/apiWatch.ts @@ -71,6 +71,14 @@ export type StopHandle = () => void const invoke = (fn: Function) => fn() +// Simple effect. +export function watchEffect( + effect: WatchEffect, + options?: BaseWatchOptions +): StopHandle { + return doWatch(effect, null, options) +} + // initial value for watchers to trigger on undefined initial values const INITIAL_WATCHER_VALUE = {} @@ -110,6 +118,13 @@ export function watch( // watch(source, cb) return doWatch(effectOrSource, cbOrOptions, options) } else { + // TODO remove this in the next release + __DEV__ && + warn( + `\`watch(fn, options?)\` signature has been moved to a separate API. ` + + `Use \`watchEffect(fn, options?)\` instead. \`watch\` will only ` + + `support \`watch(source, cb, options?) signature in the next release.` + ) // watch(effect) return doWatch(effectOrSource, null, cbOrOptions) } diff --git a/packages/runtime-core/src/index.ts b/packages/runtime-core/src/index.ts index 8629d07f..634d53b4 100644 --- a/packages/runtime-core/src/index.ts +++ b/packages/runtime-core/src/index.ts @@ -17,7 +17,7 @@ export { markNonReactive } from '@vue/reactivity' export { computed } from './apiComputed' -export { watch } from './apiWatch' +export { watch, watchEffect } from './apiWatch' export { onBeforeMount, onMounted, diff --git a/packages/vue/examples/composition/commits.html b/packages/vue/examples/composition/commits.html index 270662fe..711cbc67 100644 --- a/packages/vue/examples/composition/commits.html +++ b/packages/vue/examples/composition/commits.html @@ -22,7 +22,7 @@