Skip to content

Commit 691b76d

Browse files
committed
chore: Remeasure when scroll miss target real size
1 parent 0a74241 commit 691b76d

File tree

4 files changed

+45
-15
lines changed

4 files changed

+45
-15
lines changed

examples/basic.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ const MyItem: React.FC<Item> = ({ id }, ref) => (
1212
style={{
1313
border: '1px solid gray',
1414
padding: '0 16px',
15-
height: 30 + (id % 2 ? 0 : 10),
15+
// height: 30 + (id % 2 ? 0 : 10),
16+
height: 32,
1617
lineHeight: '30px',
1718
boxSizing: 'border-box',
1819
display: 'inline-block',
@@ -33,7 +34,7 @@ class TestItem extends React.Component<Item, {}> {
3334
}
3435

3536
const data: Item[] = [];
36-
for (let i = 0; i < 100; i += 1) {
37+
for (let i = 0; i < 200; i += 1) {
3738
data.push({
3839
id: String(i),
3940
});
@@ -46,14 +47,14 @@ const TYPES = [
4647

4748
const Demo = () => {
4849
const [destroy, setDestroy] = React.useState(false);
49-
const [visible, setVisible] = React.useState(true);
50+
const [visible, setVisible] = React.useState(false);
5051
const [type, setType] = React.useState('dom');
5152
const listRef = React.useRef<ListRef>(null);
5253

5354
React.useEffect(() => {
5455
if (visible) {
5556
listRef.current.scrollTo({
56-
index: 50,
57+
index: 100,
5758
});
5859
}
5960
}, [visible]);
@@ -156,7 +157,7 @@ const Demo = () => {
156157
ref={listRef}
157158
data={data}
158159
height={200}
159-
itemHeight={30}
160+
itemHeight={20}
160161
itemKey="id"
161162
style={{
162163
border: '1px solid red',

src/List.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,14 @@ export function RawList<T>(props: ListProps<T>, ref: React.Ref<ListRef>) {
199199
}, [inVirtual]);
200200

201201
// ================================= Ref ==================================
202-
const scrollTo = useScrollTo<T>(componentRef, mergedData, height, heights, itemHeight, getKey);
202+
const scrollTo = useScrollTo<T>(
203+
componentRef,
204+
mergedData,
205+
heights,
206+
itemHeight,
207+
getKey,
208+
collectHeight,
209+
);
203210

204211
React.useImperativeHandle(ref, () => ({
205212
scrollTo,

src/hooks/useScrollTo.tsx

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import { GetKey } from '../interface';
77
export default function useScrollTo<T>(
88
containerRef: React.RefObject<HTMLDivElement>,
99
data: T[],
10-
height: number,
1110
heights: Map<React.Key, number>,
1211
itemHeight: number,
1312
getKey: GetKey<T>,
13+
collectHeight: () => void,
1414
): ScrollTo {
1515
const scrollRef = React.useRef<number>();
1616

@@ -30,13 +30,17 @@ export default function useScrollTo<T>(
3030
}
3131

3232
// We will retry 3 times in case dynamic height shaking
33-
const syncScroll = (times = 3) => {
33+
const syncScroll = (times: number, targetAlign?: 'top' | 'bottom') => {
3434
if (times < 0 || !containerRef.current) return;
3535

36+
const height = containerRef.current.clientHeight;
37+
const mergedAlign = targetAlign || align;
38+
3639
// Get top & bottom
3740
let stackTop = 0;
3841
let itemTop = 0;
3942
let itemBottom = 0;
43+
let needCollectHeight = false;
4044

4145
for (let i = 0; i <= index; i += 1) {
4246
const key = getKey(data[i]);
@@ -45,34 +49,49 @@ export default function useScrollTo<T>(
4549
itemBottom = itemTop + (cacheHeight === undefined ? itemHeight : cacheHeight);
4650

4751
stackTop = itemBottom;
52+
53+
if (i === index && cacheHeight === undefined) {
54+
needCollectHeight = true;
55+
}
4856
}
4957

5058
// Scroll to
51-
switch (align) {
59+
let targetTop: number | null = null;
60+
let newTargetAlign: 'top' | 'bottom' | null = targetAlign;
61+
62+
switch (mergedAlign) {
5263
case 'top':
53-
containerRef.current.scrollTop = itemTop;
64+
targetTop = itemTop;
5465
break;
5566
case 'bottom':
56-
containerRef.current.scrollTop = itemBottom - height;
67+
targetTop = itemBottom - height;
5768
break;
5869

5970
default: {
6071
const { scrollTop } = containerRef.current;
6172
const scrollBottom = scrollTop + height;
6273
if (itemTop < scrollTop) {
63-
containerRef.current.scrollTop = itemTop;
74+
newTargetAlign = 'top';
6475
} else if (itemBottom > scrollBottom) {
65-
containerRef.current.scrollTop = itemBottom - height;
76+
newTargetAlign = 'bottom';
6677
}
6778
}
6879
}
6980

81+
if (targetTop !== null && targetTop !== containerRef.current.scrollTop) {
82+
containerRef.current.scrollTop = targetTop;
83+
}
84+
85+
// We will retry since element may not sync height as it described
7086
scrollRef.current = raf(() => {
71-
syncScroll(times - 1);
87+
if (needCollectHeight) {
88+
collectHeight();
89+
}
90+
syncScroll(times - 1, newTargetAlign);
7291
});
7392
};
7493

75-
syncScroll();
94+
syncScroll(3);
7695
}
7796
};
7897
}

tests/scroll.test.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ describe('List.Scroll', () => {
1515
offsetHeight: {
1616
get: () => 20,
1717
},
18+
clientHeight: {
19+
get: () => 100,
20+
},
1821
});
1922
});
2023

0 commit comments

Comments
 (0)