diff --git a/src/List.tsx b/src/List.tsx index 95b4681..05d1735 100644 --- a/src/List.tsx +++ b/src/List.tsx @@ -22,6 +22,7 @@ import type { ExtraRenderInfo, GetKey, RenderFunc, SharedConfig } from './interf import type { ScrollBarDirectionType, ScrollBarRef } from './ScrollBar'; import ScrollBar from './ScrollBar'; import { getSpinSize } from './utils/scrollbarUtil'; +import { debounce } from './utils/debounce'; const EMPTY_DATA = []; @@ -271,10 +272,15 @@ export function RawList(props: ListProps, ref: React.Ref) { rangeRef.current.start = start; rangeRef.current.end = end; + const isScrollingRef = useRef(false); // When scroll up, first visible item get real height may not same as `itemHeight`, // Which will make scroll jump. // Let's sync scroll top to avoid jump React.useLayoutEffect(() => { + // When the `scrollHeight` change is not caused by scrolling, + // end the function execution avoiding table jitter caused by changes in the first row + if (!isScrollingRef.current) return; + const changedRecord = heights.getRecord(); if (changedRecord.size === 1) { const recordKey = Array.from(changedRecord)[0]; @@ -377,6 +383,9 @@ export function RawList(props: ListProps, ref: React.Ref) { } } + const toggleScrollStatus = React.useCallback(debounce(() => { + isScrollingRef.current = false; + }, 100), []); // When data size reduce. It may trigger native scroll event back to fit scroll position function onFallbackScroll(e: React.UIEvent) { const { scrollTop: newScrollTop } = e.currentTarget; @@ -387,6 +396,10 @@ export function RawList(props: ListProps, ref: React.Ref) { // Trigger origin onScroll onScroll?.(e); triggerScroll(); + // Set the scroll status to `true` + isScrollingRef.current = true; + // Set the scroll status to `false` after scrolling ends + toggleScrollStatus(); } const keepInHorizontalRange = (nextOffsetLeft: number) => { diff --git a/src/utils/debounce.ts b/src/utils/debounce.ts new file mode 100644 index 0000000..3f45112 --- /dev/null +++ b/src/utils/debounce.ts @@ -0,0 +1,10 @@ +export function debounce(fn: (...args) => void, delay: number = 300) { + let timer; + return function (...args) { + if (timer) clearTimeout(timer); + timer = setTimeout(() => { + fn.apply(this, args); + timer = null; + }, delay); + } +} \ No newline at end of file