手把手教你用React Hook和TypeScript从零实现虚拟滚动列表组件
import React, { useState, useRef, useEffect, useCallback } from 'react';
import PropTypes from 'prop-types';
// 定义ItemRenderer类型
type ItemRenderer<T> = (item: T, index: number) => JSX.Element;
// 定义VirtualListProps类型
interface VirtualListProps<T> {
items: T[];
itemRenderer: ItemRenderer<T>;
overscanCount?: number;
itemSize?: number;
}
// 虚拟列表组件
const VirtualList = <T extends unknown>({
items,
itemRenderer,
overscanCount = 2,
itemSize = 20,
}: VirtualListProps<T>) => {
const [start, setStart] = useState(0);
const [end, setEnd] = useState(0);
const listRef = useRef<HTMLUListElement>(null);
const resetStartAndEnd = useCallback(() => {
if (listRef.current) {
const listHeight = listRef.current.clientHeight;
setStart(0);
setEnd(Math.ceil(listHeight / itemSize));
}
}, [itemSize]);
useEffect(() => {
resetStartAndEnd();
window.addEventListener('resize', resetStartAndEnd);
return () => window.removeEventListener('resize', resetStartAndEnd);
}, [resetStartAndEnd]);
// 渲染列表项
const renderedItems = items.slice(start, end + overscanCount).map((item, index) => {
const itemTop = start + index * itemSize;
const style = {
position: 'absolute',
top: `${itemTop}px`,
width: '100%',
};
return (
<li key={itemTop} style={style}>
{itemRenderer(item, index + start)}
</li>
);
});
return (
<ul ref={listRef} style={{ height: `${items.length * itemSize}px`, position: 'relative' }}>
{renderedItems}
</ul>
);
};
// 类型检查
VirtualList.propTypes = {
items: PropTypes.array.isRequired,
itemRenderer: PropTypes.func.isRequired,
overscanCount: PropTypes.number,
itemSize: PropTypes.number,
};
export default VirtualList;
这段代码实现了一个虚拟滚动列表组件,它使用React Hooks和TypeScript来提高代码质量和可维护性。组件通过监听窗口大小的变化来动态计算可视区域内应该渲染的列表项,并实现了超出视野范围的项目预渲染(overscan),以改善滚动性能和用户体验。
评论已关闭