From 8226a2dfff486e251d0d8197e9441cfa31f5dd0f 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: Wed, 8 Jul 2026 15:40:43 +0800 Subject: [PATCH] fix: cancel stale scroll retry sync --- src/Table.tsx | 17 +++++++++++++++-- tests/Table.spec.jsx | 44 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/Table.tsx b/src/Table.tsx index 06ff4464e..26f9cf3f4 100644 --- a/src/Table.tsx +++ b/src/Table.tsx @@ -453,6 +453,9 @@ const Table = ( }, []); const [setScrollTarget, getScrollTarget] = useTimeoutLock(null); + const [scrollRetryTimeoutMap] = React.useState( + () => new WeakMap>(), + ); function forceScroll(scrollLeft: number, target: HTMLDivElement | ((left: number) => void)) { if (!target) { @@ -460,16 +463,26 @@ const Table = ( } if (typeof target === 'function') { target(scrollLeft); - } else if (target.scrollLeft !== scrollLeft) { + return; + } + + const scrollRetryTimeout = scrollRetryTimeoutMap.get(target); + if (scrollRetryTimeout) { + clearTimeout(scrollRetryTimeout); + } + + if (target.scrollLeft !== scrollLeft) { target.scrollLeft = scrollLeft; // Delay to force scroll position if not sync // ref: https://github.com/ant-design/ant-design/issues/37179 - setTimeout(() => { + const retryTimeout = setTimeout(() => { if (target.scrollLeft !== scrollLeft) { target.scrollLeft = scrollLeft; } }, 0); + + scrollRetryTimeoutMap.set(target, retryTimeout); } } diff --git a/tests/Table.spec.jsx b/tests/Table.spec.jsx index c73b54761..a4d1a74c1 100644 --- a/tests/Table.spec.jsx +++ b/tests/Table.spec.jsx @@ -1365,4 +1365,48 @@ describe('Table.Basic', () => { vi.useRealTimers(); } }); + + it('cancels stale scrollLeft retry when target already synced', () => { + vi.useFakeTimers(); + + try { + const { container } = render( + createTable({ + scroll: { x: 100, y: 100 }, + }), + ); + + const tableHeader = container.querySelector('.rc-table-header'); + const tableBody = container.querySelector('.rc-table-body'); + let headerScrollLeft = 0; + let bodyScrollLeft = 0; + + Object.defineProperty(tableHeader, 'scrollLeft', { + get() { + return headerScrollLeft; + }, + set(value) { + headerScrollLeft = value; + }, + }); + Object.defineProperty(tableBody, 'scrollLeft', { + get() { + return bodyScrollLeft; + }, + }); + + bodyScrollLeft = 100; + fireEvent.scroll(tableBody); + + headerScrollLeft = 50; + bodyScrollLeft = 50; + fireEvent.scroll(tableBody); + + vi.runAllTimers(); + + expect(headerScrollLeft).toBe(50); + } finally { + vi.useRealTimers(); + } + }); });