diff --git a/.changeset/nine-chairs-care.md b/.changeset/nine-chairs-care.md new file mode 100644 index 0000000000..d87b7d5b36 --- /dev/null +++ b/.changeset/nine-chairs-care.md @@ -0,0 +1,5 @@ +--- +"@lynx-js/react": patch +--- + +Fix a memory leak when using ``. diff --git a/packages/react/runtime/src/list.ts b/packages/react/runtime/src/list.ts index 9e94b4cca8..6f3aed7ee9 100644 --- a/packages/react/runtime/src/list.ts +++ b/packages/react/runtime/src/list.ts @@ -6,6 +6,7 @@ import type { SnapshotInstance } from './snapshot.js'; export const gSignMap: Record> = {}; export const gRecycleMap: Record>> = {}; +const gParentWeakMap: WeakMap = new WeakMap(); export function clearListGlobal(): void { for (const key in gSignMap) { @@ -20,6 +21,23 @@ export function componentAtIndexFactory( ctx: SnapshotInstance[], hydrateFunction: (before: SnapshotInstance, after: SnapshotInstance) => void, ): [ComponentAtIndexCallback, ComponentAtIndexesCallback] { + // A hack workaround to ensure childCtx has no direct reference through `__parent` to list, + // to avoid memory leak. + // TODO(hzy): make `__parent` a WeakRef or `#__parent` in the future. + ctx.forEach((childCtx) => { + if (gParentWeakMap.has(childCtx)) { + // do it only once + } else { + gParentWeakMap.set(childCtx, childCtx.parentNode!); + Object.defineProperty(childCtx, '__parent', { + get: () => gParentWeakMap.get(childCtx)!, + set: (value: unknown) => { + gParentWeakMap.set(childCtx, value); + }, + }); + } + }); + const componentAtIndex = ( list: FiberElement, listID: number,