From 5ab46fe07b1c295ad81ac5658b32e21659d6fec5 Mon Sep 17 00:00:00 2001 From: Timon Lukas Date: Sat, 17 Apr 2021 15:45:39 +0200 Subject: [PATCH 1/3] feat(reactivePick): add overload for reactive picks --- packages/shared/reactivePick/index.test.ts | 67 ++++++++++++++++++++++ packages/shared/reactivePick/index.ts | 42 +++++++++++++- 2 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 packages/shared/reactivePick/index.test.ts diff --git a/packages/shared/reactivePick/index.test.ts b/packages/shared/reactivePick/index.test.ts new file mode 100644 index 00000000000..427052c1be6 --- /dev/null +++ b/packages/shared/reactivePick/index.test.ts @@ -0,0 +1,67 @@ +import { ref } from 'vue-demi' +import { reactivePick } from '.' +import { useSetup } from '../../.test' + +describe('reactivePick', () => { + it('should work with variadic parameters', () => { + useSetup(() => { + const object = { a: 'a', b: 'b' } + const pickedObject = reactivePick(object, 'a', 'b') + + expect(pickedObject.a).toEqual('a') + expect(pickedObject.b).toEqual('b') + expect((pickedObject as any).c).toBeUndefined() + + object.a = 'foo' + + expect(pickedObject.a).toEqual('foo') + }) + }) + + it('should prefer the variadic overload over the array one', () => { + useSetup(() => { + const object = { a: 'a', b: 'b' } + const pickedObject = reactivePick(object, 'a') + + expect(pickedObject.a).toEqual('a') + }) + }) + + it('should work with non-ref key arrays', () => { + useSetup(() => { + const object = { a: 'a', b: 'b', c: 'c' } + const pickedObject = reactivePick(object, ['a', 'b']) + + expect(pickedObject.a).toEqual('a') + expect(pickedObject.b).toEqual('b') + expect((pickedObject as any).c).toBeUndefined() + + object.a = 'foo' + + expect(pickedObject.a).toEqual('foo') + }) + }) + + it('should work with ref key arrays', () => { + useSetup(() => { + const object = { a: 'a', b: 'b', c: 'c' } + const picks = ref(['a' as keyof typeof object]) + const pickedObject = reactivePick(object, picks) + + expect(pickedObject.a).toEqual('a') + expect(pickedObject.b).toBeUndefined() + + object.a = 'foo' + expect(pickedObject.a).toEqual('foo') + + picks.value.push('b') + expect(pickedObject.b).toEqual('b') + + object.b = 'bar' + expect(pickedObject.b).toEqual('bar') + + picks.value.splice(1, 1) + expect(pickedObject.b).toBeUndefined() + }) + }) +}) diff --git a/packages/shared/reactivePick/index.ts b/packages/shared/reactivePick/index.ts index 7ec2015a628..a9e9d3d04fa 100644 --- a/packages/shared/reactivePick/index.ts +++ b/packages/shared/reactivePick/index.ts @@ -1,13 +1,49 @@ -import { reactive, toRef, UnwrapRef } from 'vue-demi' +import { MaybeRef, tryOnUnmounted } from '@vueuse/shared' +import { reactive, toRef, unref, UnwrapRef, set, del, watchEffect } from 'vue-demi' /** * Reactively pick fields from a reactive object * + * Overload 1: pass keys individually + * * @link https://vueuse.js.org/reactivePick + * @param obj + * @param keys */ export function reactivePick( obj: T, ...keys: K[] -): { [S in K]: UnwrapRef } { - return reactive(Object.fromEntries(keys.map(k => [k, toRef(obj, k)]))) as any +): { [S in K]: UnwrapRef } + +/** + * Reactively pick fields from a reactive object + * + * Overload 2: pass keys as (ref) array + * + * @link https://vueuse.js.org/reactivePick + * @param obj + * @param keys + */ +export function reactivePick( + obj: T, + keys: MaybeRef +): { [S in K]: UnwrapRef } + +export function reactivePick(obj: T, ...keys: any[]) { + if (keys.length !== 1 || keys[0] in obj) + return reactive(Object.fromEntries(keys.map(k => [k, toRef(obj, k)]))) + + const [keysRef]: [MaybeRef<(keyof T)[]>] = (keys as [any]) + + const reactiveObject = reactive(Object.fromEntries(unref(keysRef).map((k: keyof T) => [k, toRef(obj, k)]))) + + const stopWatch = watchEffect(() => { + const rawKeys = unref(keys[0]) + Object.keys(reactiveObject).filter(key => !rawKeys.includes(key as keyof T)).forEach(key => del(reactiveObject, key)) + rawKeys.forEach((key: keyof T) => set(reactiveObject, key, toRef(obj, key))) + }) + + tryOnUnmounted(stopWatch) + + return reactiveObject } From e760243d6139873497735281b590d3e08f958e22 Mon Sep 17 00:00:00 2001 From: Timon Lukas Date: Sat, 17 Apr 2021 15:45:53 +0200 Subject: [PATCH 2/3] docs(reactivePick): document new overload --- packages/shared/reactivePick/index.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/shared/reactivePick/index.md b/packages/shared/reactivePick/index.md index cf3d959e508..f56548afba2 100644 --- a/packages/shared/reactivePick/index.md +++ b/packages/shared/reactivePick/index.md @@ -19,6 +19,10 @@ const obj = reactive({ }) const picked = reactivePick(obj, 'x', 'elementX') // { x: number, elementX: number } +// or +const pickedArr = reactivePick(obj, ['x', 'elementX']) // { x: number, elementX: number } +// or +const pickedArrRef = reactivePick(obj, ref(['x', 'elementX'])) // { x: number, elementX: number } ``` ### Scenarios @@ -43,6 +47,8 @@ const props = defineProps({ }) const childProps = reactivePick(props, 'color', 'font') +// or +const childPropsArr = reactivePick(props, Object.keys(ChildComp.props))