import React, { useState, useEffect, useCallback, useRef } from "react";
import { List, useDynamicRowHeight } from "react-window";
const mockFetchData = (page, action = "load") => {
return new Promise((resolve) => {
setTimeout(() => {
const startIndex = (page - 1) * 20 + 1;
const newData = Array.from({ length: 20 }, (_, i) => {
const id = action === "refresh" ? `new_${page}_${i}` : startIndex + i;
const lines = ((startIndex + i) % 4) + 1;
const content = `数据项 ${startIndex + i} ` + "内容 ".repeat(lines);
return { id, content, lines };
});
resolve(newData);
}, 500);
});
};
const VirtualListWithScrollLoad = () => {
const [dataList, setDataList] = useState([]);
const [loading, setLoading] = useState(false);
const [page, setPage] = useState(1);
const [finished, setFinished] = useState(false);
const containerHeight = 400;
const listRef = useRef(null);
const dynamicRowHeight = useDynamicRowHeight({ defaultRowHeight: 60 });
const didInitRef = useRef(false);
const inFlightRef = useRef(false);
const loadMore = useCallback(async () => {
console.log("loadMore", page);
if (inFlightRef.current || loading || finished) return;
inFlightRef.current = true;
setLoading(true);
try {
const newData = await mockFetchData(page, "load");
if (newData.length === 0) {
setFinished(true);
} else {
setDataList((prev) => [...prev, ...newData]);
setPage((prev) => prev + 1);
}
} catch (error) {
console.error("加载数据失败:", error);
} finally {
setLoading(false);
inFlightRef.current = false;
}
}, [loading, finished]);
useEffect(() => {
if (didInitRef.current) return;
didInitRef.current = true;
loadMore();
}, [loadMore]);
const handleScroll = useCallback(
(e) => {
const el = e.currentTarget;
const { scrollTop, scrollHeight, clientHeight } = el;
if (
scrollHeight - scrollTop - clientHeight < 100 &&
!loading &&
!finished
) {
loadMore();
}
},
[loading, finished, loadMore]
);
const Row = ({ index, style, items }) => {
const item = items[index];
if (!item) return <div style={style} />;
const lines = item.lines || 1;
return (
<div
style={{
...style,
borderBottom: "1px solid #eee",
padding: "8px 12px",
boxSizing: "border-box",
}}
>
<div style={{ fontWeight: 600 }}>数据项 {item.id}</div>
{Array.from({ length: lines }).map((_, i) => (
<div key={i}>内容</div>
))}
</div>
);
};
return (
<div>
<List
style={{ height: containerHeight, width: "100%" }}
overscanCount={5}
rowCount={dataList.length}
rowHeight={dynamicRowHeight}
rowComponent={Row}
rowProps={{ items: dataList }}
listRef={listRef}
onScroll={handleScroll}
/>
{/* 加载指示器 */}
<div style={{ textAlign: "center", padding: "10px" }}>
{loading && <div>加载中...</div>}
{finished && <div>没有更多数据了</div>}
</div>
</div>
);
};
export default VirtualListWithScrollLoad;