hooks
hooks copied to clipboard
useVirtualList 最后一个元素的高度不能超过 container 的高度
当最后一个元素的高度比 Container 还高时,getOffset 的返回值会等于 list.length
const getOffset = (scrollTop: number) => {
if (isNumber(itemHeightRef.current)) {
return Math.floor(scrollTop / itemHeightRef.current) + 1;
}
let sum = 0;
let offset = 0;
for (let i = 0; i < list.length; i++) {
const height = itemHeightRef.current(i, list[i]);
sum += height;
if (sum >= scrollTop) {
offset = i;
break;
}
}
return offset + 1;
};
在计算 visibleCount 时,会导致 getVisibleCount 的返回值为 -list.length
const getVisibleCount = (containerHeight: number, fromIndex: number) => {
if (isNumber(itemHeightRef.current)) {
return Math.ceil(containerHeight / itemHeightRef.current);
}
let sum = 0;
let endIndex = 0;
for (let i = fromIndex; i < list.length; i++) {
const height = itemHeightRef.current(i, list[i]);
sum += height;
endIndex = i;
if (sum >= containerHeight) {
break;
}
}
return endIndex - fromIndex;
};
最终会导致 start = list.length,end = overscan,会取不到元素进行渲染。
const start = Math.max(0, offset - overscan); // 起始位置
const end = Math.min(list.length, offset + visibleCount + overscan); // 结束位置
建议在 getVisibleCount 返回时增加判断 endIndex - fromIndex < 0 ? 0 : endIndex - fromIndex, 这样在最后一个元素高度比 Contianer 还大时,仍然可以保证正常的渲染。
用官网的例子改了改就可以复现。
/**
* title: Dynamic item height
* desc: Specify item height dynamically.
*
* title.zh-CN: 动态元素高度
* desc.zh-CN: 动态指定每个元素的高度
*/
import React, { useMemo, useRef } from 'react';
import { useVirtualList } from 'ahooks';
export default () => {
const containerRef = useRef(null);
const wrapperRef = useRef(null);
const originalList = useMemo(() => Array.from(Array(200).keys()), []);
const [value, onChange] = React.useState<number>(0);
const [list, scrollTo] = useVirtualList(originalList, {
containerTarget: containerRef,
wrapperTarget: wrapperRef,
itemHeight: (i) => (i % 2 === 0 ? 428 : 310),
overscan: 10,
});
return (
<div>
<div style={{ textAlign: 'right', marginBottom: 16 }}>
<input
style={{ width: 120 }}
placeholder="line number"
type="number"
value={value}
onChange={(e) => onChange(Number(e.target.value))}
/>
<button
style={{ marginLeft: 8 }}
type="button"
onClick={() => {
scrollTo(Number(value));
}}
>
scroll to
</button>
</div>
<div ref={containerRef} style={{ height: '300px', overflow: 'auto', background: 'black' }}>
<div ref={wrapperRef}>
{list.map((ele) => (
<div
style={{
height: ele.index % 2 === 0 ? 428 : 310,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
border: '1px solid #e8e8e8',
background: ele.index % 2 === 0 ? 'green' : 'red'
}}
key={ele.index}
>
Row: {ele.data+1} size: {ele.index % 2 === 0 ? 'small' : 'large'}
</div>
))}
</div>
</div>
</div>
);
};
let me try