diff --git a/.changeset/plenty-kiwis-live.md b/.changeset/plenty-kiwis-live.md new file mode 100644 index 0000000000..d9842e15d7 --- /dev/null +++ b/.changeset/plenty-kiwis-live.md @@ -0,0 +1,5 @@ +--- +"@lynx-js/react": patch +--- + +Should only recycle off-screen `list-item` in recursive hydration. diff --git a/packages/react/runtime/__test__/list.test.jsx b/packages/react/runtime/__test__/list.test.jsx index ac8d0bc6fb..8496f884e4 100644 --- a/packages/react/runtime/__test__/list.test.jsx +++ b/packages/react/runtime/__test__/list.test.jsx @@ -1774,6 +1774,11 @@ describe('list reload', () => { hydrate(b, bb); b.unRenderElements(); + // Should only update `list-item` in the recycling pool + // Should not add the on-screen `list-item` to the recycling pool, + expect([...recycleSignMap.keys()]).toStrictEqual([__GetElementUniqueID(d3.__element_root)]); + expect(recycleSignMap.get(__GetElementUniqueID(d3.__element_root))).not.toBe(d3); + // The one rendered should be removed expect(root).toMatchInlineSnapshot(` @@ -1940,7 +1945,11 @@ describe('list reload', () => { expect(signMap.get(__GetElementUniqueID(d1.__element_root))).toBe(d2_); // note: d1 is reused by d2_ expect(signMap.get(__GetElementUniqueID(d2_.__element_root))).toBe(d2_); expect(signMap.get(__GetElementUniqueID(d3_.__element_root))).toBe(d3_); - expect(recycleSignMap.get(__GetElementUniqueID(d3_.__element_root))).toBe(d3_); + // d1 was enqueued as well, so it should be in the recycling pool + expect([...recycleSignMap.keys()]).toStrictEqual([ + __GetElementUniqueID(d3.__element_root), + __GetElementUniqueID(d1.__element_root), + ]); }); }); diff --git a/packages/react/runtime/src/hydrate.ts b/packages/react/runtime/src/hydrate.ts index d161a16b31..bbdca7453b 100644 --- a/packages/react/runtime/src/hydrate.ts +++ b/packages/react/runtime/src/hydrate.ts @@ -332,7 +332,12 @@ export function hydrate(before: SnapshotInstance, after: SnapshotInstance, optio } if (recycleMap.has(a.type)) { const recycleSignMap = recycleMap.get(a.type)!; - recycleSignMap.set(listItemID, b); + // Should only update `list-item` in the recycling pool + // Because if an on-screen `list-item` is added to the recycling pool, + // it could cause a blank screen when reused next time, as it may still be visible. + if (recycleSignMap.has(listItemID)) { + recycleSignMap.set(listItemID, b); + } } } },