Skip to content

Commit 1fc8263

Browse files
committed
fix(): fix non-relevant nodes been removed when re-rendering nested :if
1 parent e3d80a2 commit 1fc8263

File tree

5 files changed

+137
-4
lines changed

5 files changed

+137
-4
lines changed

cypress/e2e/control-nodes.spec.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,5 +263,29 @@ for (const port of Cypress.env("ports")) {
263263

264264
cy.get("@console.error").should("not.be.called");
265265
});
266+
267+
it("should handle nested :if rerender", () => {
268+
cy.visit(`${origin}/e2e/nested-if`, {
269+
onBeforeLoad(win) {
270+
cy.spy(win.console, "error").as("console.error");
271+
},
272+
});
273+
274+
cy.expectMainContents([
275+
"Click 1Click 2Second button is not clicked",
276+
"This should always be shown",
277+
]);
278+
279+
cy.contains("Click 1").click();
280+
cy.expectMainContents([
281+
"Click 1Click 2Second button is not clicked",
282+
"This should always be shown",
283+
]);
284+
285+
cy.contains("Click 2").click();
286+
cy.expectMainContents(["Click 1Click 2", "This should always be shown"]);
287+
288+
cy.get("@console.error").should("not.be.called");
289+
});
266290
});
267291
}

mock-micro-apps/e2e/storyboard.yaml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,3 +1040,60 @@ routes:
10401040
# - path: '${APP.homepage}/initial-rerender/apps/:group?'
10411041
# exact: true
10421042
# bricks: []
1043+
1044+
- path: '${APP.homepage}/nested-if'
1045+
context:
1046+
- name: value
1047+
value: null
1048+
- name: alwaysFalsy
1049+
value: <% !!CTX.value %>
1050+
track: true
1051+
- name: secondButtonClicked
1052+
value: false
1053+
bricks:
1054+
- brick: div
1055+
iid: 1
1056+
children:
1057+
- brick: p
1058+
iid: 2
1059+
children:
1060+
- brick: button
1061+
iid: 3
1062+
properties:
1063+
textContent: Click 1
1064+
events:
1065+
click:
1066+
- action: context.replace
1067+
args:
1068+
- value
1069+
- false
1070+
- brick: button
1071+
iid: 4
1072+
properties:
1073+
textContent: Click 2
1074+
events:
1075+
click:
1076+
- action: context.replace
1077+
args:
1078+
- secondButtonClicked
1079+
- true
1080+
- brick: :if
1081+
iid: 5
1082+
dataSource: <%= !CTX.secondButtonClicked %>
1083+
children:
1084+
- brick: p
1085+
iid: 6
1086+
properties:
1087+
textContent: Second button is not clicked
1088+
- brick: :if
1089+
iid: 7
1090+
dataSource: <%= CTX.alwaysFalsy %>
1091+
children:
1092+
- brick: p
1093+
iid: 8
1094+
properties:
1095+
textContent: This should never be shown
1096+
- brick: p
1097+
iid: 9
1098+
properties:
1099+
textContent: This should always be shown

packages/runtime/src/internal/Renderer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,7 @@ async function legacyRenderBrick(
587587

588588
rawOutput!.node ??= {
589589
tag: RenderTag.PLACEHOLDER,
590+
iid: brickConf.iid,
590591
return: returnNode,
591592
tracking,
592593
};

packages/runtime/src/internal/RendererContext.ts

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,13 @@ export class RendererContext {
160160
}
161161
>
162162
>;
163+
#reversedMemoized?: WeakMap<
164+
RenderChildNode,
165+
Array<{
166+
returnNode: RenderNode;
167+
memKey: string;
168+
}>
169+
>;
163170

164171
#initialMenuRequestNode?: MenuRequestNode;
165172
#memoizedMenuRequestNodeMap?: WeakMap<
@@ -404,22 +411,47 @@ export class RendererContext {
404411
this.#memoized.set(returnNode, mem);
405412
}
406413

414+
const last = getLastNode(node);
415+
const lastNormal = getLastNormalNode(node);
416+
const lastPortal = getLastPortalNode(node);
417+
418+
this.#reverseMemoize(returnNode, memKey, last);
419+
407420
mem.set(memKey, {
408421
node,
409-
last: getLastNode(node),
410-
lastNormal: getLastNormalNode(node),
411-
lastPortal: getLastPortalNode(node),
422+
last,
423+
lastNormal,
424+
lastPortal,
412425
});
413426
}
414427

428+
#reverseMemoize(
429+
returnNode: RenderReturnNode,
430+
memKey: string,
431+
last: RenderChildNode | undefined
432+
) {
433+
if (last) {
434+
if (!this.#reversedMemoized) {
435+
this.#reversedMemoized = new WeakMap();
436+
}
437+
let list = this.#reversedMemoized.get(last);
438+
if (!list) {
439+
this.#reversedMemoized.set(last, (list = []));
440+
}
441+
list.push({
442+
returnNode,
443+
memKey,
444+
});
445+
}
446+
}
447+
415448
reRender(
416449
slotId: string | undefined,
417450
keyPath: number[],
418451
node: RenderChildNode | undefined,
419452
returnNode: RenderReturnNode
420453
) {
421454
const memKey = [slotId ?? "", ...keyPath].join(".");
422-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
423455
const memoized = this.#memoized!.get(returnNode)!.get(memKey)!;
424456
const {
425457
node: prevNode,
@@ -460,6 +492,23 @@ export class RendererContext {
460492
memoized.lastNormal = getLastNormalNode(node);
461493
memoized.lastPortal = getLastPortalNode(node);
462494

495+
if (prevLast) {
496+
const prevList = this.#reversedMemoized?.get(prevLast);
497+
for (const item of prevList ?? []) {
498+
const mem = this.#memoized!.get(item.returnNode)!.get(item.memKey);
499+
if (mem) {
500+
mem.last = last;
501+
502+
if (mem.lastNormal === prevLast && memoized.lastNormal === last) {
503+
mem.lastNormal = last;
504+
}
505+
}
506+
}
507+
this.#reversedMemoized?.delete(prevLast);
508+
}
509+
510+
this.#reverseMemoize(returnNode, memKey, last);
511+
463512
// Figure out the unchanged prev sibling and next sibling
464513
let prevSibling: RenderChildNode | undefined;
465514
let current = returnNode.child;
@@ -565,6 +614,7 @@ export class RendererContext {
565614
this.#mediaListener = undefined;
566615
}
567616
this.#memoized = undefined;
617+
this.#reversedMemoized = undefined;
568618
this.#arbitraryLifeCycle.clear();
569619
this.#incrementalRenderStates.clear();
570620
this.#memoizedMenuRequestNodeMap = undefined;

packages/runtime/src/internal/interfaces.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export interface RenderPlaceholder extends BaseRenderNode {
6868
tag: RenderTag.PLACEHOLDER;
6969
return: RenderReturnNode;
7070
tracking?: boolean;
71+
iid?: string;
7172
}
7273

7374
export interface BaseRenderNode {

0 commit comments

Comments
 (0)