# 简单实现VirtualList组件

# 实现思路

  1. 根据需要展示的列表总数据计算出滚动总高度,用一个锚点元素配合tranform:translateY()撑出滚动条。
<div style={{ transform: `translateY(${list.length * itemHeight}px)`, height: 1 }}></div>
  1. 计算出视口需要展示的列表数据
useEffect(() => {
    setVisibleList(list.slice(firstIndex, lastIndex));
}, [list, firstIndex, lastIndex])
  1. 滚动事件触发后的处理器函数
 const handleScroll = useCallback(() => {
    const firstIndex = Math.floor(containerRef.current.scrollTop / itemHeight);
    const lastIndex = firstIndex + Math.ceil(containerHeight / itemHeight) + 4;
    setFirstIndex(firstIndex);
    setLastIndex(lastIndex);
  }, []);
  1. 配合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
作者:王龙楷; 标签:原创; 提交时间: 5/23/2022, 8:48:37 PM