架构设计
- 状态管理:使用 React 的
useState
或状态管理库(如 Redux、MobX)来管理列表数据。例如,使用 useState
定义一个数组来存储列表项数据,同时定义一个状态来表示加载状态(loading)。
import React, { useState, useEffect } from 'react';
const InfiniteScrollList = () => {
const [listData, setListData] = useState([]);
const [loading, setLoading] = useState(false);
// 其他逻辑...
};
- 滚动监听:使用
useEffect
结合 window.addEventListener('scroll', callback)
来监听滚动事件。当滚动到页面底部一定距离时,触发加载新数据的操作。
useEffect(() => {
const handleScroll = () => {
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
const clientHeight = document.documentElement.clientHeight;
const scrollHeight = document.documentElement.scrollHeight;
if (scrollTop + clientHeight >= scrollHeight - 100 &&!loading) {
setLoading(true);
// 加载新数据的逻辑
}
};
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, [loading]);
- 数据更新处理:当数据新增、删除或修改时,通过更新
listData
状态来触发 React 的重新渲染。为了确保滚动位置不受影响,可以记录当前滚动位置,在数据更新后恢复滚动位置。
// 新增数据
const addData = newData => {
const currentScrollTop = document.documentElement.scrollTop || document.body.scrollTop;
setListData([...listData, newData]);
setTimeout(() => {
document.documentElement.scrollTop = currentScrollTop;
}, 0);
};
// 删除数据
const deleteData = index => {
const currentScrollTop = document.documentElement.scrollTop || document.body.scrollTop;
const newListData = listData.filter((_, i) => i!== index);
setListData(newListData);
setTimeout(() => {
document.documentElement.scrollTop = currentScrollTop;
}, 0);
};
// 修改数据
const updateData = (index, updatedData) => {
const currentScrollTop = document.documentElement.scrollTop || document.body.scrollTop;
const newListData = [...listData];
newListData[index] = updatedData;
setListData(newListData);
setTimeout(() => {
document.documentElement.scrollTop = currentScrollTop;
}, 0);
};
关键代码实现思路
- 滚动监听:在
useEffect
中添加和移除滚动事件监听器,通过计算滚动高度、可视区域高度和文档总高度来判断是否接近页面底部,从而触发加载新数据。
- 数据更新:无论是新增、删除还是修改数据,都先记录当前滚动位置,更新状态后使用
setTimeout
在微任务队列中恢复滚动位置。这是因为 React 的状态更新是异步的,直接在更新状态后设置滚动位置可能会导致无效操作,setTimeout
确保在 DOM 更新完成后再恢复滚动位置。
- 加载状态管理:通过
loading
状态来避免重复触发加载新数据的操作,确保每次滚动加载时只有在当前没有处于加载状态下才会执行加载逻辑。