From 39329f80b21bf20a3279ef87b2df33a9878f5a5d Mon Sep 17 00:00:00 2001 From: Samuel Degueldre Date: Thu, 8 Dec 2022 13:55:01 +0100 Subject: [PATCH] [FIX] reactivity: don't subscribe to keys when making reactive When attempting to create a reactive object, we first check if the target can be made reactive, this is done with Object.toString, which internally reads the Symbol.toStringTag on the underlying object. When trying to make a reactive object from another, for example when reobserving a reactive or when reading a reactive object from the context of another, this would read the subscribe the original object to the Symbol.toStringTag property. This commit fixes that by calling Object.toString on the underlying target object where applicable. --- src/runtime/reactivity.ts | 2 +- tests/reactivity.test.ts | 28 +++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/runtime/reactivity.ts b/src/runtime/reactivity.ts index ffe33493..19610535 100644 --- a/src/runtime/reactivity.ts +++ b/src/runtime/reactivity.ts @@ -28,7 +28,7 @@ const COLLECTION_RAWTYPES = new Set(["Set", "Map", "WeakMap"]); * @returns the raw type of the object */ function rawType(obj: any) { - return objectToString.call(obj).slice(8, -1); + return objectToString.call(toRaw(obj)).slice(8, -1); } /** * Checks whether a given value can be made into a reactive object. diff --git a/tests/reactivity.test.ts b/tests/reactivity.test.ts index a02648fa..9d67f1d5 100644 --- a/tests/reactivity.test.ts +++ b/tests/reactivity.test.ts @@ -9,7 +9,7 @@ import { markRaw, toRaw, } from "../src"; -import { reactive } from "../src/runtime/reactivity"; +import { reactive, getSubscriptions } from "../src/runtime/reactivity"; import { batched } from "../src/runtime/utils"; import { makeDeferred, @@ -1020,6 +1020,32 @@ describe("Reactivity", () => { expect(n3).toBe(2); }); + test("reactive inside other: reading the inner reactive from outer doesn't affect the inner's subscriptions", async () => { + const getObservedKeys = (obj: any) => getSubscriptions(obj).flatMap(({ keys }) => keys); + let n1 = 0; + let n2 = 0; + const innerCb = () => n1++; + const outerCb = () => n2++; + const inner = createReactive({ a: 1 }, innerCb); + const outer = createReactive({ b: inner }, outerCb); + expect(n1).toBe(0); + expect(n2).toBe(0); + expect(getObservedKeys(innerCb)).toEqual([]); + expect(getObservedKeys(outerCb)).toEqual([]); + + outer.b.a; + expect(getObservedKeys(innerCb)).toEqual([]); + expect(getObservedKeys(outerCb)).toEqual(["b", "a"]); + expect(n1).toBe(0); + expect(n2).toBe(0); + + outer.b.a = 2; + expect(getObservedKeys(innerCb)).toEqual([]); + expect(getObservedKeys(outerCb)).toEqual([]); + expect(n1).toBe(0); + expect(n2).toBe(1); + }); + // test("notification is not done after unregistration", async () => { // let n = 0; // const observer = () => n++;