Skip to content

Commit 13ba33a

Browse files
committed
fix(): dispose abstract nodes
1 parent b78dbdd commit 13ba33a

File tree

3 files changed

+47
-18
lines changed

3 files changed

+47
-18
lines changed

packages/runtime/src/internal/Renderer.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ async function legacyRenderBrick(
347347
returnNode,
348348
{
349349
brick: ":if",
350+
iid: brickConf.iid,
350351
dataSource: brickIf,
351352
// `permissionsPreCheck` maybe required before computing `if`.
352353
permissionsPreCheck,
@@ -463,11 +464,12 @@ async function legacyRenderBrick(
463464
(slots[slot] as SlotConfOfBricks)?.bricks;
464465

465466
const output = getEmptyRenderOutput();
466-
const controlNode: RenderAbstract = {
467+
const abstractNode: RenderAbstract = {
467468
tag: RenderTag.ABSTRACT,
468469
return: returnNode,
470+
iid: brickConf.iid,
469471
};
470-
output.node = controlNode;
472+
output.node = abstractNode;
471473

472474
if (!Array.isArray(bricks)) {
473475
return output;
@@ -481,7 +483,7 @@ async function legacyRenderBrick(
481483
break;
482484
}
483485
childrenOutput = await renderForEach(
484-
controlNode,
486+
abstractNode,
485487
computedDataSource,
486488
bricks,
487489
runtimeContext,
@@ -497,7 +499,7 @@ async function legacyRenderBrick(
497499
case ":if":
498500
case ":switch": {
499501
childrenOutput = await renderBricks(
500-
controlNode,
502+
abstractNode,
501503
bricks,
502504
runtimeContext,
503505
rendererContext,
@@ -511,7 +513,7 @@ async function legacyRenderBrick(
511513
}
512514

513515
if (childrenOutput) {
514-
controlNode.child = childrenOutput.node;
516+
abstractNode.child = childrenOutput.node;
515517
mergeRenderOutput(output, { ...childrenOutput, node: undefined });
516518
}
517519

@@ -641,7 +643,7 @@ async function legacyRenderBrick(
641643
trailing: true,
642644
});
643645
const runtimeBrick =
644-
returnNode.tag === RenderTag.BRICK ? returnNode : null;
646+
returnNode.tag === RenderTag.ROOT ? null : returnNode;
645647
const disposes = runtimeBrick ? (runtimeBrick.disposes ??= []) : [];
646648
if (contextNames) {
647649
for (const contextName of contextNames) {
@@ -899,11 +901,11 @@ async function legacyRenderBrick(
899901
}
900902

901903
let lastOutput = getEmptyRenderOutput();
902-
const controlNode: RenderAbstract = {
904+
const abstractNode: RenderAbstract = {
903905
tag: RenderTag.ABSTRACT,
904906
return: brick,
905907
};
906-
lastOutput.node = controlNode;
908+
lastOutput.node = abstractNode;
907909

908910
const parentRoute = parentRoutes[parentRoutes.length - 1] as
909911
| RouteConfOfBricks
@@ -1034,7 +1036,7 @@ async function legacyRenderBrick(
10341036
}
10351037

10361038
const routesOutput = await renderRoutes(
1037-
controlNode,
1039+
abstractNode,
10381040
slotConf.routes,
10391041
childRuntimeContext,
10401042
rendererContext,
@@ -1045,7 +1047,7 @@ async function legacyRenderBrick(
10451047
initialTracker
10461048
);
10471049

1048-
controlNode.child = routesOutput.node;
1050+
abstractNode.child = routesOutput.node;
10491051
mergeRenderOutput(output, { ...routesOutput, node: undefined });
10501052
appendMenuRequestNode(
10511053
menuRequestReturnNode,

packages/runtime/src/internal/RendererContext.ts

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { getReadOnlyProxy } from "./proxyFactories.js";
1515
import { Media, mediaEventTarget } from "./mediaQuery.js";
1616
import type {
1717
MenuRequestNode,
18+
RenderAbstract,
1819
RenderBrick,
1920
RenderChildNode,
2021
RenderNode,
@@ -359,6 +360,12 @@ export class RendererContext {
359360
}
360361
}
361362

363+
#unmountAbstracts(abstracts: Set<RenderAbstract>) {
364+
for (const item of abstracts) {
365+
item.disposes?.forEach((dispose) => dispose());
366+
}
367+
}
368+
362369
#initializeRerenderBricks(bricks: Set<RenderBrick>): void {
363370
const mountEvent = new CustomEvent("mount");
364371
for (const { brick, handlers } of this.#memoizedLifeCycle.onMount) {
@@ -429,9 +436,11 @@ export class RendererContext {
429436
current = current.sibling;
430437
}
431438

432-
// Unmount previous bricks, including their descendants
433-
const removeBricks = getContainedBricks(oldNode);
439+
// Unmount previous brick and abstract nodes, including their descendants
440+
const [removeBricks, removeAbstracts] =
441+
getContainedBrickAndAbstractNodes(oldNode);
434442
this.#unmountBricks(removeBricks);
443+
this.#unmountAbstracts(removeAbstracts);
435444

436445
mountTree(renderRoot);
437446

@@ -443,6 +452,11 @@ export class RendererContext {
443452
}
444453
node.sibling = oldNode.sibling;
445454

455+
// Clean up old node
456+
delete oldNode.child;
457+
delete oldNode.sibling;
458+
delete (oldNode as Partial<RenderChildNode>).return;
459+
446460
// Resume `return`
447461
current = node;
448462
while (current) {
@@ -486,7 +500,7 @@ export class RendererContext {
486500
portal.insertBefore(portalFragment, insertPortalBeforeChild);
487501
}
488502

489-
const newBricks = getContainedBricks(node);
503+
const [newBricks] = getContainedBrickAndAbstractNodes(node);
490504
this.#initializeRerenderBricks(newBricks);
491505
}
492506

@@ -768,12 +782,17 @@ function findNextSiblingNode(
768782
}
769783
}
770784

771-
function getContainedBricks(node: RenderChildNode) {
772-
const range = new Set<RenderBrick>();
785+
function getContainedBrickAndAbstractNodes(
786+
node: RenderChildNode
787+
): [bricks: Set<RenderBrick>, abstracts: Set<RenderAbstract>] {
788+
const bricks = new Set<RenderBrick>();
789+
const abstracts = new Set<RenderAbstract>();
773790
let current: RenderNode | undefined = node;
774791
while (current) {
775792
if (current.tag === RenderTag.BRICK) {
776-
range.add(current);
793+
bricks.add(current);
794+
} else if (current.tag === RenderTag.ABSTRACT) {
795+
abstracts.add(current);
777796
}
778797
if (current.child) {
779798
current = current.child;
@@ -787,7 +806,7 @@ function getContainedBricks(node: RenderChildNode) {
787806
while (currentReturn) {
788807
if (currentReturn === node) {
789808
// End the loop when encounter the original node.
790-
return range;
809+
return [bricks, abstracts];
791810
}
792811
if (currentReturn.sibling) {
793812
break;
@@ -797,13 +816,19 @@ function getContainedBricks(node: RenderChildNode) {
797816
current = currentReturn?.sibling;
798817
}
799818
}
800-
return range;
819+
return [bricks, abstracts];
801820
}
802821

803822
function getEntityReturnNode(node: RenderReturnNode): RenderRoot | RenderBrick {
804823
let current = node;
805824
while (current.tag === RenderTag.ABSTRACT) {
806825
current = current.return;
826+
// istanbul ignore next
827+
if (!current) {
828+
throw new Error(
829+
"Cannot find render root node. This is a bug of Brick Next, please report it."
830+
);
831+
}
807832
}
808833
return current;
809834
}

packages/runtime/src/internal/interfaces.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ export interface RenderBrick extends BaseRenderNode, RuntimeBrick {
6666
export interface RenderAbstract extends BaseRenderNode {
6767
tag: RenderTag.ABSTRACT;
6868
return: RenderReturnNode;
69+
iid?: string;
70+
disposes?: (() => void)[];
6971
}
7072

7173
export interface BaseRenderNode {

0 commit comments

Comments
 (0)