diff --git a/packages/components/hooks/useVirtualScroll.ts b/packages/components/hooks/useVirtualScroll.ts index 1ebc24be2e..fe9252fcb3 100644 --- a/packages/components/hooks/useVirtualScroll.ts +++ b/packages/components/hooks/useVirtualScroll.ts @@ -28,6 +28,7 @@ const requestAnimationFrame = const useVirtualScroll = (container: MutableRefObject, params: UseVirtualScrollParams) => { const { data, scroll } = params; + const isUpdating = useRef(false); const dataRef = useRef(data); const containerHeight = useRef(0); @@ -70,6 +71,8 @@ const useVirtualScroll = (container: MutableRefObject, params: UseV }; const updateVisibleData = (trScrollTopHeightList: number[], scrollTop: number) => { + if (isUpdating.current) return; + let currentIndex = -1; // 获取当前滚动到哪一个元素(大数据场景不建议使用 forEach 一类函数迭代) for (let i = 0, len = trScrollTopHeightList.length; i < len; i++) { @@ -117,13 +120,26 @@ const useVirtualScroll = (container: MutableRefObject, params: UseV } if (startAndEndIndex.join() !== [startIndex, endIndex].join() && startIndex >= 0) { + isUpdating.current = true; + const tmpVisibleData = fixedStartData.concat(data.slice(startIndex, endIndex)).concat(fixedEndData); setVisibleData(tmpVisibleData); - const lastScrollTop = trScrollTopHeightList[startIndex - 1]; - const top = lastScrollTop > 0 ? lastScrollTop : 0; + + const topOffset = trScrollTopHeightList[startIndex - 1] ?? 0; const stickyHeight = trScrollTopHeightList[Math.min(startIndex, fixedStart) - 1] || 0; - setTranslateY(top - stickyHeight); - setStartAndEndIndex([startIndex, endIndex]); + setTranslateY(topOffset - stickyHeight); // 先计当前帧的位移 + setStartAndEndIndex([startIndex, endIndex]); // 再更新索引,确保数值稳定 + + const targetScrollTop = scrollTop; + setTimeout(() => { + const currentScrollTop = container.current?.scrollTop; + const scrollTopDiff = Math.abs(currentScrollTop - targetScrollTop); + if (scrollTopDiff > 0) { + // eslint-disable-next-line no-param-reassign + container.current.scrollTop = targetScrollTop; + } + isUpdating.current = false; + }, 0); } }; @@ -242,7 +258,7 @@ const useVirtualScroll = (container: MutableRefObject, params: UseV trScrollTopHeightList.current = scrollTopHeightList; clearTimeout(timer); } - }, 1); + }, 0); }, // eslint-disable-next-line [container, data, tScroll, isVirtualScroll, startAndEndIndex, trHeightList], diff --git a/packages/components/table/TBody.tsx b/packages/components/table/TBody.tsx index cc2441392a..118a339827 100644 --- a/packages/components/table/TBody.tsx +++ b/packages/components/table/TBody.tsx @@ -81,7 +81,7 @@ export default function TBody(props: TableBodyProps) { const [global, t] = useLocaleReceiver('table'); - const { skipSpansMap } = useRowspanAndColspan(data, columns, rowKey, props.rowspanAndColspan); + const { skipSpansMap } = useRowspanAndColspan(renderData, columns, rowKey, props.rowspanAndColspan); const isSkipSnapsMapNotFinish = Boolean(props.rowspanAndColspan && !skipSpansMap.size); const { tableFullRowClasses, tableBaseClass } = allTableClasses; diff --git a/packages/components/table/hooks/useRowspanAndColspan.ts b/packages/components/table/hooks/useRowspanAndColspan.ts index b9ee13e1d9..db15e96750 100644 --- a/packages/components/table/hooks/useRowspanAndColspan.ts +++ b/packages/components/table/hooks/useRowspanAndColspan.ts @@ -1,7 +1,8 @@ -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import { get } from 'lodash-es'; import log from '@tdesign/common-js/log/index'; -import { BaseTableCellParams, BaseTableCol, TableRowData, TableRowspanAndColspanFunc } from '../type'; +import useIsomorphicLayoutEffect from '../../hooks/useLayoutEffect'; +import type { BaseTableCellParams, BaseTableCol, TableRowData, TableRowspanAndColspanFunc } from '../type'; export interface SkipSpansValue { colspan?: number; @@ -80,7 +81,7 @@ export default function useRowspanAndColspan( return map; }; - useEffect(() => { + useIsomorphicLayoutEffect(() => { if (!rowspanAndColspan) return; skipSpansMap.clear(); const result = getSkipSpansMap(data, columns, rowspanAndColspan);