const App = () => {
const content = useRef(null)
const scrollBox = useRef(null)
const scrollList = useRef(null)
const [data, setData] = useState([])
const [start, setStart] = useState(0)
const [end, setEnd] = useState(0)
const scrollInfo = useRef({
boxHeight: 500,
itemHeight: 50,
renderCount: 0,
bufferSize: 8
})
useEffect(() => {
const res = new Array(2000).fill(1).map((item, index) => item + index)
setData(res)
const {
itemHeight, boxHeight, bufferSize} = scrollInfo.current
const renderCount = Math.ceil(boxHeight / itemHeight) + bufferSize
scrollInfo.current.renderCount = renderCount
setEnd(renderCount)
}, [])
const handleScroll = () => {
const {
itemHeight, boxHeight, bufferSize, renderCount} = scrollInfo.current
const {
scrollTop} = scrollBox.current
const bufferVal = bufferSize / 2
const newStartIndex = Math.floor(scrollTop / itemHeight)
const newEndIndex = newStartIndex + renderCount
if (newEndIndex !== end || newStartIndex !== start) {
setStart(newStartIndex)
setEnd(newEndIndex)
}
const currentOffset = scrollTop - (scrollTop % itemHeight)
scrollList.current.style.transform = `translate3d(0, ${
currentOffset}px, 0)`
}
const renderList = data.slice(start, end)
const {
itemHeight} = scrollInfo.current
return <div ref={
content}>
<div ref={
scrollBox} className="scroll_box" onScroll={
handleScroll}>
{
}
<div className="scroll_hold" style={
{
height: `${
data.length * itemHeight}px`}}></div>
<ul className="list" ref={
scrollList}>
{
renderList.map(item => <li key={
item} style={
{
height: '50px'}}>{
item}</li>)
}
</ul>
</div>
</div>
}
.scroll_box {
overflow-y: scroll;
position: relative;
height: 500px;
}
.scroll_hold {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.list{
position: absolute;
left: 0;
right: 0;
top: 0;
}
li{
width: 100%;
list-style: none;
padding: 5px;
}