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

React中react-infinite-scroller无限滚动触发过多请求致更新深度超限

问题分析与修复方案

核心问题拆解

  1. fetchData参数未生效:定义了fetchData(pageNumber)但请求里用的是组件的page状态,异步场景下容易出现状态不一致,导致请求逻辑混乱。
  2. pageStart与初始page不匹配:InfiniteScroll的pageStart设为0,但初始page是1,组件内部的分页计数和状态不同步,会触发额外加载逻辑。
  3. 无请求锁机制:滚动触发loadMore后,请求完成前可能重复触发loadMore,导致短时间内大量API调用。
  4. 内容高度不足触发无限加载:如果渲染的列表总高度没填满滚动容器,InfiniteScroll会一直认为未滚动到底,持续调用loadMore。

修复步骤

1. 修正fetchData的参数使用

将请求里的page改为传入的pageNumber,确保每次请求用触发时的页码:

const fetchData = async (pageNumber) => {
  try {
    const response = await axiosInstance.get("/search/?page=" + pageNumber)
    const newData = response.data.results;
    setData(prevData => [...prevData, ...newData]);
    setHasMore(response.data.next !== null);
  } catch (error) {
    console.error('Error fetching data:', error);
    // 请求失败时停止加载,避免无限重试
    setHasMore(false);
  }
};

2. 同步pageStart与初始page

把InfiniteScroll的pageStart改成1,和初始page状态保持一致:

<InfiniteScroll
  pageStart={1} // 改为1
  loadMore={loadMore}
  hasMore={hasMore}
  loader={<div key={0}>Loading...</div>}
>

3. 添加请求锁防止重复调用

用useRef创建请求锁,标记当前是否正在请求,避免同一时间多次触发:

import React, { useState, useEffect, useRef } from 'react';

// ... 其他代码
const isFetching = useRef(false);

const fetchData = async (pageNumber) => {
  if (isFetching.current) return;
  isFetching.current = true;
  
  try {
    const response = await axiosInstance.get("/search/?page=" + pageNumber)
    const newData = response.data.results;
    setData(prevData => [...prevData, ...newData]);
    setHasMore(response.data.next !== null);
  } catch (error) {
    console.error('Error fetching data:', error);
    setHasMore(false);
  } finally {
    // 请求完成后释放锁
    isFetching.current = false;
  }
};

4. 确保滚动容器与内容高度正确

把InfiniteScroll放到Box内部,给Box添加overflow: 'auto',同时确保列表项有足够高度:

{data.map(item => (
  <div key={item.id} style={{height: '100px', padding: '1rem', borderBottom: '1px solid #eee'}}>
    {/* 渲染你的数据内容 */}
  </div>
))}

5. 优化初始加载逻辑

初始加载直接调用fetchData(1),避免依赖page状态的副作用:

useEffect(() => {
  fetchData(1);
}, []); // 空依赖,仅执行一次

const loadMore = () => {
  const nextPage = page + 1;
  fetchData(nextPage);
  setPage(nextPage);
};

完整修复后的代码

import React, { useState, useEffect, useRef } from 'react';
import InfiniteScroll from 'react-infinite-scroller';
import Box from '@mui/material/Box';
import axiosInstance from '../axios';

const Test = () => {
  const [data, setData] = useState([]);
  const [hasMore, setHasMore] = useState(true);
  const [page, setPage] = useState(1);
  const isFetching = useRef(false);

  const fetchData = async (pageNumber) => {
    if (isFetching.current) return;
    isFetching.current = true;

    try {
      const response = await axiosInstance.get("/search/?page=" + pageNumber);
      const newData = response.data.results;
      setData(prevData => [...prevData, ...newData]);
      // 额外判断新数据不为空,避免空数据时仍认为有更多内容
      setHasMore(!!response.data.next && newData.length > 0);
    } catch (error) {
      console.error('Error fetching data:', error);
      setHasMore(false);
    } finally {
      isFetching.current = false;
    }
  };

  useEffect(() => {
    fetchData(1);
  }, []);

  const loadMore = () => {
    const nextPage = page + 1;
    fetchData(nextPage);
    setPage(nextPage);
  };

  return (
    <>
      <Box sx={{height: '800px', overflow: 'auto'}}>
        <InfiniteScroll
          pageStart={1}
          loadMore={loadMore}
          hasMore={hasMore}
          loader={<div key={0}>Loading...</div>}
        >
          {data.map(item => (
            <div key={item.id} style={{height: '100px', padding: '1rem', borderBottom: '1px solid #eee'}}>
              {/* 渲染你的数据,比如{item.title} */}
            </div>
          ))}
        </InfiniteScroll>
      </Box>
    </>
  );
};

export default Test;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 21:30:14