From 2f8788803584436ca0abfb023105dec4f880c170 Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Thu, 27 Jul 2023 16:28:25 +0200 Subject: [PATCH 1/2] fix(toRefs): don't trigger unwanted effects --- packages/shared/toRefs/index.test.ts | 40 +++++++++++++++++++++++++++- packages/shared/toRefs/index.ts | 35 +++++++++++++++++------- 2 files changed, 65 insertions(+), 10 deletions(-) diff --git a/packages/shared/toRefs/index.test.ts b/packages/shared/toRefs/index.test.ts index 06475052177..233133221d8 100644 --- a/packages/shared/toRefs/index.test.ts +++ b/packages/shared/toRefs/index.test.ts @@ -1,4 +1,4 @@ -import { computed, isVue3, reactive, ref } from 'vue-demi' +import { computed, isVue3, reactive, ref, watchSyncEffect } from 'vue-demi' import { describe, expect, it, vi } from 'vitest' import { toRefs } from '.' @@ -98,6 +98,44 @@ describe('toRefs', () => { expect(spy).toHaveBeenLastCalledWith(['a', 1]) }) + it('should trigger unwanted effects with replaceRef = false', () => { + const spy = vi.fn() + const obj = ref({ + a: 'a', + b: 0, + }) + + const { a, b } = toRefs(obj, { replaceRef: true }) + expect(a.value).toBe('a') + expect(b.value).toBe(0) + + watchSyncEffect(() => spy(a.value)) + + expect(spy).toHaveBeenCalledTimes(1) + + b.value = 1 + expect(spy).toHaveBeenCalledTimes(2) + }) + + it('should not trigger unwanted effects with replaceRef = false', () => { + const spy = vi.fn() + const obj = ref({ + a: 'a', + b: 0, + }) + + const { a, b } = toRefs(obj, { replaceRef: false }) + expect(a.value).toBe('a') + expect(b.value).toBe(0) + + watchSyncEffect(() => spy(a.value)) + + expect(spy).toHaveBeenCalledTimes(1) + + b.value = 1 + expect(spy).toHaveBeenCalledTimes(1) + }) + it('should save instance of class', () => { class SomeClass { v = 1 diff --git a/packages/shared/toRefs/index.ts b/packages/shared/toRefs/index.ts index f3aab110b57..2b62d35e1d3 100644 --- a/packages/shared/toRefs/index.ts +++ b/packages/shared/toRefs/index.ts @@ -2,6 +2,15 @@ import type { ToRefs } from 'vue-demi' import { toRefs as _toRefs, customRef, isRef } from 'vue-demi' import type { MaybeRef } from '../utils' +export interface ToRefsOptions { + /** + * Replace the original ref with a copy on property update. + * + * @default true + */ + replaceRef?: boolean +} + /** * Extended `toRefs` that also accepts refs of an object. * @@ -10,6 +19,7 @@ import type { MaybeRef } from '../utils' */ export function toRefs( objectRef: MaybeRef, + options: ToRefsOptions = {}, ): ToRefs { if (!isRef(objectRef)) return _toRefs(objectRef) @@ -18,23 +28,30 @@ export function toRefs( ? new Array(objectRef.value.length) : {} + const { replaceRef = true } = options + for (const key in objectRef.value) { result[key] = customRef(() => ({ get() { return objectRef.value[key] }, set(v) { - if (Array.isArray(objectRef.value)) { - const copy: any = [...objectRef.value] - copy[key] = v - objectRef.value = copy - } - else { - const newObject = { ...objectRef.value, [key]: v } + if (replaceRef) { + if (Array.isArray(objectRef.value)) { + const copy: any = [...objectRef.value] + copy[key] = v + objectRef.value = copy + } + else { + const newObject = { ...objectRef.value, [key]: v } - Object.setPrototypeOf(newObject, Object.getPrototypeOf(objectRef.value)) + Object.setPrototypeOf(newObject, Object.getPrototypeOf(objectRef.value)) - objectRef.value = newObject + objectRef.value = newObject + } + } + else { + objectRef.value[key] = v } }, })) From 07270301d3ab17f3b542906d7d97a6b6ce6f650a Mon Sep 17 00:00:00 2001 From: Kasper Seweryn Date: Thu, 27 Jul 2023 17:34:08 +0200 Subject: [PATCH 2/2] feat(toRefs): make `replaceRef` option a `MaybeRefOrGetter` --- packages/shared/toRefs/index.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/shared/toRefs/index.ts b/packages/shared/toRefs/index.ts index 2b62d35e1d3..96935dfe075 100644 --- a/packages/shared/toRefs/index.ts +++ b/packages/shared/toRefs/index.ts @@ -1,6 +1,7 @@ import type { ToRefs } from 'vue-demi' import { toRefs as _toRefs, customRef, isRef } from 'vue-demi' -import type { MaybeRef } from '../utils' +import { toValue } from '../toValue' +import type { MaybeRef, MaybeRefOrGetter } from '../utils' export interface ToRefsOptions { /** @@ -8,7 +9,7 @@ export interface ToRefsOptions { * * @default true */ - replaceRef?: boolean + replaceRef?: MaybeRefOrGetter } /** @@ -28,14 +29,14 @@ export function toRefs( ? new Array(objectRef.value.length) : {} - const { replaceRef = true } = options - for (const key in objectRef.value) { result[key] = customRef(() => ({ get() { return objectRef.value[key] }, set(v) { + const replaceRef = toValue(options.replaceRef) ?? true + if (replaceRef) { if (Array.isArray(objectRef.value)) { const copy: any = [...objectRef.value]