# 简单实现VirtualList组件
# 实现思路
- 根据需要展示的列表总数据计算出滚动总高度,用一个锚点元素配合
tranform:translateY()撑出滚动条。<div style={{ transform: `translateY(${list.length * itemHeight}px)`, height: 1 }}></div>
- 计算出视口需要展示的列表数据
useEffect(() => { setVisibleList(list.slice(firstIndex, lastIndex)); }, [list, firstIndex, lastIndex])
- 滚动事件触发后的处理器函数
const handleScroll = useCallback(() => { const firstIndex = Math.floor(containerRef.current.scrollTop / itemHeight); const lastIndex = firstIndex + Math.ceil(containerHeight / itemHeight) + 4; setFirstIndex(firstIndex); setLastIndex(lastIndex); }, []);
- 配合
transform:translateeY()展示列表数据visibleList.map((item, index) => { return <div key={index} style={{ transform: `translateY(${firstIndex * itemHeight}px)` }} > {item} </div>; })
# VirtualList组件源代码
import { useCallback, useEffect, useState, useRef } from "react";
const VList = (props) => {
const { containerHeight, itemHeight, list } = props;
const [firstIndex, setFirstIndex] = useState(0);
const [lastIndex, setLastIndex] = useState(Math.ceil(containerHeight / itemHeight) + 4);
const [visibleList, setVisibleList] = useState([]);
const containerRef = useRef();
useEffect(() => {
setVisibleList(list.slice(firstIndex, lastIndex));
}, [list, firstIndex, lastIndex])
const handleScroll = useCallback(() => {
const firstIndex = Math.floor(containerRef.current.scrollTop / itemHeight);
const lastIndex = firstIndex + Math.ceil(containerHeight / itemHeight) + 4;
setFirstIndex(firstIndex);
setLastIndex(lastIndex);
}, []);
return <div
ref={containerRef}
style={{ height: containerHeight, overflow: 'auto' }}
onScroll={handleScroll}
>
<div style={{ transform: `translateY(${list.length * itemHeight}px)`, height: 1 }}></div>
{
visibleList.map((item, index) => {
return <div
key={index}
style={{ transform: `translateY(${firstIndex * itemHeight}px)` }}
>
{item}
</div>;
})
}
</div>;
}
export default VList;
# 使用举例
import VList from "./VList";
function App() {
const createList = () => {
let Arr = [];
for (let i = 0; i < 1000; i++) {
Arr.push(
<div style={{ lineHeight: '50px', textAlign: 'center' }}>
item---{i + 1}
</div>
)
}
return Arr;
}
return (
<div>
<VList list={createList()} containerHeight={500} itemHeight={50} />
</div>
)
}
export default App