Skip to content

Commit 34d8db5

Browse files
authored
enhance: Scroll top should not shaking (#296)
* docs: update docs * chore: sync back * chore: use promise to enhance scroll logic * docs: back
1 parent 42dd61e commit 34d8db5

File tree

3 files changed

+45
-4
lines changed

3 files changed

+45
-4
lines changed

src/List.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,26 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
271271
rangeRef.current.start = start;
272272
rangeRef.current.end = end;
273273

274+
// When scroll up, first visible item get real height may not same as `itemHeight`,
275+
// Which will make scroll jump.
276+
// Let's sync scroll top to avoid jump
277+
React.useLayoutEffect(() => {
278+
const changedRecord = heights.getRecord();
279+
if (changedRecord.size === 1) {
280+
const recordKey = Array.from(changedRecord)[0];
281+
const startIndexKey = getKey(mergedData[start]);
282+
if (startIndexKey === recordKey) {
283+
const realStartHeight = heights.get(recordKey);
284+
const diffHeight = realStartHeight - itemHeight;
285+
syncScrollTop((ori) => {
286+
return ori + diffHeight;
287+
});
288+
}
289+
}
290+
291+
heights.resetRecord();
292+
}, [scrollHeight]);
293+
274294
// ================================= Size =================================
275295
const [size, setSize] = React.useState({ width: 0, height });
276296

src/hooks/useHeights.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import findDOMNode from 'rc-util/lib/Dom/findDOMNode';
2-
import raf from 'rc-util/lib/raf';
32
import * as React from 'react';
43
import { useEffect, useRef } from 'react';
54
import type { GetKey } from '../interface';
@@ -23,10 +22,11 @@ export default function useHeights<T>(
2322
const [updatedMark, setUpdatedMark] = React.useState(0);
2423
const instanceRef = useRef(new Map<React.Key, HTMLElement>());
2524
const heightsRef = useRef(new CacheMap());
26-
const collectRafRef = useRef<number>();
25+
26+
const promiseIdRef = useRef<number>(0);
2727

2828
function cancelRaf() {
29-
raf.cancel(collectRafRef.current);
29+
promiseIdRef.current += 1;
3030
}
3131

3232
function collectHeight(sync = false) {
@@ -56,7 +56,13 @@ export default function useHeights<T>(
5656
if (sync) {
5757
doCollect();
5858
} else {
59-
collectRafRef.current = raf(doCollect);
59+
promiseIdRef.current += 1;
60+
const id = promiseIdRef.current;
61+
Promise.resolve().then(() => {
62+
if (id === promiseIdRef.current) {
63+
doCollect();
64+
}
65+
});
6066
}
6167
}
6268

src/utils/CacheMap.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,33 @@ class CacheMap {
88
// `useMemo` no need to update if `id` not change
99
id: number = 0;
1010

11+
diffKeys = new Set<React.Key>();
12+
1113
constructor() {
1214
this.maps = Object.create(null);
1315
}
1416

1517
set(key: React.Key, value: number) {
1618
this.maps[key as string] = value;
1719
this.id += 1;
20+
this.diffKeys.add(key as string);
1821
}
1922

2023
get(key: React.Key) {
2124
return this.maps[key as string];
2225
}
26+
27+
/**
28+
* CacheMap will record the key changed.
29+
* To help to know what's update in the next render.
30+
*/
31+
resetRecord() {
32+
this.diffKeys.clear();
33+
}
34+
35+
getRecord() {
36+
return this.diffKeys;
37+
}
2338
}
2439

2540
export default CacheMap;

0 commit comments

Comments
 (0)