From e101fb8ebfa676ecac00b7a8850b5ecbd73e3ed0 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Sun, 29 Jan 2023 01:22:23 +0100 Subject: [PATCH] feat(usePrevious): new function --- packages/core/usePrevious/index.md | 23 ++++++++++++ packages/core/usePrevious/index.test.ts | 47 +++++++++++++++++++++++++ packages/core/usePrevious/index.ts | 27 ++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 packages/core/usePrevious/index.md create mode 100644 packages/core/usePrevious/index.test.ts create mode 100644 packages/core/usePrevious/index.ts diff --git a/packages/core/usePrevious/index.md b/packages/core/usePrevious/index.md new file mode 100644 index 00000000000..1539477689d --- /dev/null +++ b/packages/core/usePrevious/index.md @@ -0,0 +1,23 @@ +--- +category: Utilities +--- + +# usePrevious + +Holds the previous value of a ref. + +## Usage + +```ts +import { ref } from 'vue' +import { usePrevious } from '@vueuse/core' + +const counter = ref('Hello') +const previous = usePrevious(counter) + +console.log(previous.value) // undefined + +counter.value = 'World' + +console.log(previous.value) // Hello +``` diff --git a/packages/core/usePrevious/index.test.ts b/packages/core/usePrevious/index.test.ts new file mode 100644 index 00000000000..5d78c7d87b7 --- /dev/null +++ b/packages/core/usePrevious/index.test.ts @@ -0,0 +1,47 @@ +import { describe, expect, it } from 'vitest' +import { ref } from 'vue-demi' +import { usePrevious } from '.' + +describe('usePrevious', () => { + it('works for literals', () => { + const target = ref(1) + const previous = usePrevious(target) + + expect(previous.value).toBe(undefined) + + target.value = 2 + + expect(previous.value).toBe(1) + + target.value = 10 + + expect(previous.value).toBe(2) + }) + + it('works with initial value', () => { + const target = ref('Hello') + const previous = usePrevious(() => target.value, 'initial') + + expect(previous.value).toBe('initial') + + target.value = 'World' + + expect(previous.value).toBe('Hello') + }) + + it('works with object', () => { + const target = ref({ a: 1 }) + const previous = usePrevious(target) + + expect(previous.value).toEqual(undefined) + + target.value.a = 2 + + // nested reactiveness is not triggered + expect(previous.value).toEqual(undefined) + + target.value = { b: 2 } + + expect(previous.value).toEqual({ a: 2 }) + }) +}) diff --git a/packages/core/usePrevious/index.ts b/packages/core/usePrevious/index.ts new file mode 100644 index 00000000000..349a3f66a21 --- /dev/null +++ b/packages/core/usePrevious/index.ts @@ -0,0 +1,27 @@ +/* This implementation is original ported from https://github.com/shorwood/pompaute by Stanley Horwood */ + +import type { Ref } from 'vue-demi' +import { readonly, shallowRef, watch } from 'vue-demi' +import type { MaybeComputedRef } from '@vueuse/shared' +import { resolveRef } from '@vueuse/shared' + +/** + * Holds the previous value of a ref. + * + * @see {@link https://vueuse.org/usePrevious} + */ +export function usePrevious(value: MaybeComputedRef): Readonly> +export function usePrevious(value: MaybeComputedRef, initialValue: T): Readonly> +export function usePrevious(value: MaybeComputedRef, initialValue?: T) { + const previous = shallowRef(initialValue) + + watch( + resolveRef(value), + (_, oldValue) => { + previous.value = oldValue + }, + { flush: 'sync' }, + ) + + return readonly(previous) +}