From 0bde454379d316d177d2d2ba9c0ead087f526b70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=B6=E8=BF=9C=E6=96=B9?= Date: Tue, 28 Mar 2023 18:03:49 +0800 Subject: [PATCH] feat(reactivePick): add predicate parameter (#2850) --- packages/shared/reactivePick/index.md | 19 ++++++++++- packages/shared/reactivePick/index.test.ts | 37 ++++++++++++++++++++++ packages/shared/reactivePick/index.ts | 18 +++++++++-- 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/packages/shared/reactivePick/index.md b/packages/shared/reactivePick/index.md index d6737506efc..3fc7101c019 100644 --- a/packages/shared/reactivePick/index.md +++ b/packages/shared/reactivePick/index.md @@ -8,7 +8,9 @@ Reactively pick fields from a reactive object. ## Usage -```js +### Basic Usage + +```ts import { reactivePick } from '@vueuse/core' const obj = reactive({ @@ -21,6 +23,21 @@ const obj = reactive({ const picked = reactivePick(obj, 'x', 'elementX') // { x: number, elementX: number } ``` +### Predicate Usage + +```ts +import { reactivePick } from '@vueuse/core' +const source = reactive({ + foo: 'foo', + bar: 'bar', + baz: 'baz', + qux: true, +}) +const state = reactivePick(source, (value, key) => key !== 'bar' && value !== true) +// { foo: string, baz: string } +source.qux = false +// { foo: string, baz: string, qux: boolean } +```` ### Scenarios #### Selectively passing props to child diff --git a/packages/shared/reactivePick/index.test.ts b/packages/shared/reactivePick/index.test.ts index 3cd091cc996..a1b5ac8910c 100644 --- a/packages/shared/reactivePick/index.test.ts +++ b/packages/shared/reactivePick/index.test.ts @@ -44,4 +44,41 @@ describe('reactivePick', () => { bar: 'bar2', }) }) + it('should work with predicate', () => { + const source = reactive({ + foo: 'foo', + bar: 'bar', + baz: 'baz', + qux: true, + }) + const state = reactivePick(source, (value, key) => { + return key !== 'bar' && value !== true + }) + + expect(state).toEqual({ + foo: 'foo', + baz: 'baz', + }) + + source.foo = 'foo2' + + expect(state).toEqual({ + foo: 'foo2', + baz: 'baz', + }) + + source.bar = 'bar1' + + expect(state).toEqual({ + foo: 'foo2', + baz: 'baz', + }) + + source.qux = false + expect(state).toEqual({ + foo: 'foo2', + baz: 'baz', + qux: false, + }) + }) }) diff --git a/packages/shared/reactivePick/index.ts b/packages/shared/reactivePick/index.ts index 37808fba1ab..f507b2b7179 100644 --- a/packages/shared/reactivePick/index.ts +++ b/packages/shared/reactivePick/index.ts @@ -1,5 +1,18 @@ import type { UnwrapRef } from 'vue-demi' -import { reactive, toRef } from 'vue-demi' +import { toRef, toRefs } from 'vue-demi' +import { reactiveComputed } from '../reactiveComputed' +import { resolveUnref } from '../resolveUnref' + +export type ReactivePickPredicate = (value: T[keyof T], key: keyof T) => boolean + +export function reactivePick( + obj: T, + ...keys: (K | K[])[] +): { [S in K]: UnwrapRef } +export function reactivePick( + obj: T, + predicate: ReactivePickPredicate +): { [S in keyof T]?: UnwrapRef } /** * Reactively pick fields from a reactive object @@ -11,5 +24,6 @@ export function reactivePick( ...keys: (K | K[])[] ): { [S in K]: UnwrapRef } { const flatKeys = keys.flat() as K[] - return reactive(Object.fromEntries(flatKeys.map(k => [k, toRef(obj, k)]))) as any + const predicate = flatKeys[0] as unknown as ReactivePickPredicate + return reactiveComputed(() => typeof predicate === 'function' ? Object.fromEntries(Object.entries(toRefs(obj)).filter(([k, v]) => predicate(resolveUnref(v) as T[K], k as K))) : Object.fromEntries(flatKeys.map(k => [k, toRef(obj, k)]))) as any }