From 84327641748ee4d78e6b899726d32c9ac88a3fc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Thu, 30 Jul 2026 18:57:22 +0800 Subject: [PATCH 1/3] fix: resolve scroll key against latest data --- src/hooks/useScrollTo.tsx | 35 ++++++++++++++++++++--------------- tests/scroll.test.js | 25 +++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/hooks/useScrollTo.tsx b/src/hooks/useScrollTo.tsx index ce143c9..4ca3d2f 100644 --- a/src/hooks/useScrollTo.tsx +++ b/src/hooks/useScrollTo.tsx @@ -61,14 +61,15 @@ export default function useScrollTo( ): (arg: number | ScrollTarget) => void { const scrollRef = React.useRef(undefined); - const [syncState, setSyncState] = React.useState<{ - times: number; - index: number; - offset: ScrollOffset; - originAlign: ScrollAlign; - targetAlign?: 'top' | 'bottom'; - lastTop?: number; - }>(null); + const [syncState, setSyncState] = React.useState< + { + times: number; + offset: ScrollOffset; + originAlign: ScrollAlign; + targetAlign?: 'top' | 'bottom'; + lastTop?: number; + } & ({ index: number } | { key: React.Key }) + >(null); // ========================== Sync Scroll ========================== useLayoutEffect(() => { @@ -81,17 +82,21 @@ export default function useScrollTo( collectHeight(); - const { targetAlign, originAlign, index, offset: rawOffset } = syncState; + const { targetAlign, originAlign, offset: rawOffset } = syncState; + const index = + 'key' in syncState + ? data.findIndex((item) => getKey(item) === syncState.key) + : syncState.index; const mergedAlign = targetAlign || originAlign; const offset = getOffset(rawOffset, { getSize, align: mergedAlign }); const height = containerRef.current.clientHeight; - let needCollectHeight = false; + let needCollectHeight = 'key' in syncState && index < 0; let newTargetAlign: 'top' | 'bottom' | null = targetAlign; let targetTop: number | null = null; // Go to next frame if height not exist - if (height) { + if (height && index >= 0) { // Get top & bottom let stackTop = 0; let itemTop = 0; @@ -186,20 +191,20 @@ export default function useScrollTo( if (typeof arg === 'number') { syncScrollTop(arg); } else if (arg && typeof arg === 'object') { - let index: number; + let target: { index: number } | { key: React.Key }; const { align } = arg; if ('index' in arg) { - ({ index } = arg); + target = { index: arg.index }; } else { - index = data.findIndex((item) => getKey(item) === arg.key); + target = { key: arg.key }; } const { offset: rawOffset = 0 } = arg; setSyncState({ times: 0, - index, + ...target, offset: rawOffset, originAlign: align, }); diff --git a/tests/scroll.test.js b/tests/scroll.test.js index f9dd1d2..0c1ea28 100644 --- a/tests/scroll.test.js +++ b/tests/scroll.test.js @@ -208,6 +208,31 @@ describe('List.Scroll', () => { expect(container.querySelector('ul').scrollTop).toEqual(520); }); + it('refreshes key index when data changes', () => { + const ref = React.createRef(); + + function Demo() { + const [data, setData] = React.useState(genData(1)); + + return ( + <> +