最近初学了react和大致了解了虚拟列表的实现,就手写实现一下练练手。
主要功能要点:
- 自适应容器
- 虚拟滚动列表
- 可配置组件
上代码:
import { useRef, useEffect, useState } from "react";
export default function Test() {
const data = new Array(1000).fill(0).map((item, i) => i);
const scrollConfig = {
itemHeight: 20,
maxCount: 15,
total: data.length,
};
return (
<div className="wrap">
<VirtualScroll
dataSource={data}
scrollConfig={scrollConfig}
></VirtualScroll>
</div>
);
}
function VirtualScroll({ dataSource, scrollConfig }) {
const scrollRef = useRef(null);
let startIndex = 0;
let endIndex = 0;
const [myData, setMyData] = useState([]);
const [y, setY] = useState(0);
const contentWrapStyle = {
height: scrollConfig.itemHeight * scrollConfig.total + "px",
};
const itemWrapStyle = {
transform: ` translateY(${y})`,
};
useEffect(() => {
const scrollDom = scrollRef.current;
const handleScroll = () => {
const top = scrollDom.scrollTop;
const newY = top + "px";
setY(newY);
startIndex = Math.round(top / scrollConfig.itemHeight);
endIndex = startIndex + scrollConfig.maxCount;
const newData = dataSource.slice(startIndex, endIndex);
setMyData(newData);
console.log(top, startIndex, endIndex, myData);
};
scrollDom && scrollDom.addEventListener("scroll", handleScroll);
return () => {
scrollDom && scrollDom.removeEventListener("scroll", handleScroll);
};
}, []);
return (
<div className="scrollWrap" ref={scrollRef}>
<div className="contentWrap" style={contentWrapStyle}></div>
<div className="itemWrap" style={itemWrapStyle}>
{myData.map((item, i) => (
<ScrollItem
content={item}
key={i}
itemHeight={scrollConfig.itemHeight}
/>
))}
</div>
</div>
);
}
function ScrollItem({ content, itemHeight }) {
return (
<>
<div style={{ height: itemHeight + "px" }}>{content}</div>
</>
);
}
css样式如下:
.wrap{
width: 200px;
height: 300px;
margin: 30px auto;
}
.scrollWrap {
background: #afece3;
border: 1px solid #999;
width: 100%;
height: 100%;
overflow-y: scroll;
position: relative;
left: 0;
top: 0;
}
.itemWrap{
width: 100%;
position: absolute;
top: 0;
left: 0;
}
简易效果图: