From ec6f8b1379dc71031805a2e3046691b0cd7f43d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Luba=C5=84ski?= Date: Mon, 8 Jan 2024 11:24:02 +0100 Subject: [PATCH] fix(cache): Clear deps and context when get value from cache (#230) --- src/cache.js | 33 ++++++++++++++++++++--------- test/spec/define.js | 51 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 10 deletions(-) diff --git a/src/cache.js b/src/cache.js index d27baea1..3b356911 100644 --- a/src/cache.js +++ b/src/cache.js @@ -10,18 +10,10 @@ function dispatch(entry) { while (entry) { entry.resolved = false; - if (entry.deps) { - for (const depEntry of entry.deps) { - depEntry.contexts.delete(entry); - } - entry.deps.clear(); - } - if (entry.contexts) { for (const context of entry.contexts) { - if (!stack.has(context)) { - if (!contexts.includes(context)) contexts.push(context); - entry.contexts.delete(context); + if (!stack.has(context) && !contexts.includes(context)) { + contexts.push(context); } } } @@ -79,6 +71,13 @@ export function get(target, key, getter) { if (entry.resolved) return entry.value; + if (entry.deps) { + for (const depEntry of entry.deps) { + depEntry.contexts.delete(entry); + } + entry.deps.clear(); + } + const lastContext = context; try { @@ -173,6 +172,20 @@ function invalidateEntry(entry, options) { } if (options.deleteEntry) { + if (entry.deps) { + for (const depEntry of entry.deps) { + depEntry.contexts.delete(entry); + } + entry.deps = undefined; + } + + if (entry.contexts) { + for (const context of entry.contexts) { + context.deps.delete(entry); + } + entry.contexts = undefined; + } + deleteEntry(entry); } } diff --git a/test/spec/define.js b/test/spec/define.js index 39895a85..9a46015a 100644 --- a/test/spec/define.js +++ b/test/spec/define.js @@ -165,6 +165,57 @@ describe("define:", () => { }); }); + // Relates to https://github.com/hybridsjs/hybrids/issues/229 + // with a move of clearing deps and context in cache + // There is still a problem with "prop3" which is not updated in render + // but it happens because the render was already called after the prop1 observer + it("render method is called when observed chain of properties changes", () => { + define({ + tag: "test-define-render-observed", + prop1: { + value: 0, + observe(host, value) { + host.prop2 = value; + }, + }, + prop2: { + value: 0, + observe(host, value) { + host.prop3 = value; + }, + }, + prop3: 0, + render: ({ prop1, prop2, prop3 }) => + // prettier-ignore + html`
${prop1}
${prop2}
${prop3}
`, + }); + + el = document.createElement("test-define-render-observed"); + document.body.appendChild(el); + + return resolveRaf(() => { + expect(el.shadowRoot.innerHTML).toBe( + "
0
0
0
", + ); + + el.prop1 = 1; + + return resolveRaf(() => { + expect(el.shadowRoot.innerHTML).toBe( + "
1
1
0
", + ); + + el.prop1 = 2; + + return resolveRaf(() => { + expect(el.shadowRoot.innerHTML).toBe( + "
2
2
1
", + ); + }); + }); + }); + }); + describe("created element", () => { beforeAll(() => { define({