You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React无限滚动加载时如何从指定ID起始获取API数据避免重复

问题核心原因

你当前出现重复数据的问题本质是分页逻辑错误,和API是否支持自定义起始点无关:

  • 首屏请求路径为/friends/1/20,对应接口规则是第1页、每页20条,成功拉取前20条数据后,你没有更新分页状态,初始page值一直停留在1
  • 滚动触发加载时,你传入的页码还是1,且单页条数改成了40,相当于重复请求第1页的40条数据,自然会包含首屏已经加载的前20条
  • 同时你直接取闭包内的items做数组合并,在滚动连续触发时很容易拿到旧状态,进一步加剧重复、数据丢失问题

关于你提到的「能否自行选择数据起始点,仅拉取ID大于等于指定值的数据」,核心取决于后端API的能力:如果API开放了min_id/since_id/offset这类过滤参数,传参即可实现服务端过滤,性能最优;如果API未开放相关能力,前端无法要求服务端只返回指定范围数据,只能拉取结果后在前端做过滤、去重。

可落地方案

方案1:修正分页逻辑(适配你当前使用的接口,无冗余请求)

你当前使用的接口是标准的页码分页接口,路径规则为/user/{用户id}/friends/{页码}/{每页条数},不需要额外传ID过滤参数,只要对齐分页规则即可彻底解决重复问题:

  • 统一每页请求的条数,避免首屏拉20条、滚动加载拉40条的参数不一致问题
  • 首屏加载完第1页数据后,把分页状态更新为2,下一次请求直接从第2页开始拉取
  • 用React函数式状态更新规避闭包陷阱,同时加请求锁避免连续触发重复请求

修正后的核心代码如下:

const params = useParams();
const [items, setItems] = useState([]);
// 统一每页加载条数
const PAGE_SIZE = 20;
const [page, setPage] = useState(1);
const [isFetching, setIsFetching] = useState(false);

// 首屏加载第一页好友数据
const fetchInitialData = () => {
  setIsFetching(true);
  const url = `http://sweeftdigital-intern.eu-central-1.elasticbeanstalk.com/user/${params.id}/friends/1/${PAGE_SIZE}`;
  axios.get(url).then((res) => {
    setItems(res.data.list);
    // 第一页加载完成,页码更新为2,下一次直接拉第二页
    setPage(2);
    setIsFetching(false);
  });
};

// 组件挂载时触发首屏加载
useEffect(() => {
  fetchInitialData();
}, [params.id]);

// 滚动触发加载更多
const loadMoreData = () => {
  // 请求进行中直接返回,避免重复触发
  if (isFetching) return;
  setIsFetching(true);
  const url = `http://sweeftdigital-intern.eu-central-1.elasticbeanstalk.com/user/${params.id}/friends/${page}/${PAGE_SIZE}`;
  axios.get(url).then(res => {
    // 函数式更新,避免闭包拿到旧的items值
    setItems(prevItems => [...prevItems,...res.data.list]);
    // 页码自增
    setPage(prevPage => prevPage + 1);
    setIsFetching(false);
  });
}

改完后首屏加载1-20条,第一次滚动加载21-40条,第二次滚动加载41-60条,完全不会出现重复数据。

方案2:前端去重兜底(适配不支持过滤、分页参数不规范的接口)

如果后续遇到不支持标准分页、也没有ID过滤能力的接口,可以在前端做唯一键去重兜底,哪怕接口返回重复数据也不会重复渲染:

axios.get(url).then(res => {
  setItems(prevItems => {
    // 提取已经加载过的所有好友唯一ID
    const existedIds = new Set(prevItems.map(item => item.id));
    // 过滤掉已经存在的重复条目
    const uniqueNewItems = res.data.list.filter(item =>!existedIds.has(item.id));
    return [...prevItems,...uniqueNewItems];
  });
  setPage(prevPage => prevPage + 1);
  setIsFetching(false);
})

这种方案的缺点是会拉取到已经加载过的冗余数据,浪费请求流量,仅作为兜底方案使用。


内容的提问来源于stack exchange,提问作者ceaiius

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 16:06:21