React中react-infinite-scroller无限滚动触发过多请求致更新深度超限
问题分析与修复方案
核心问题拆解
- fetchData参数未生效:定义了
fetchData(pageNumber)但请求里用的是组件的page状态,异步场景下容易出现状态不一致,导致请求逻辑混乱。 - pageStart与初始page不匹配:
InfiniteScroll的pageStart设为0,但初始page是1,组件内部的分页计数和状态不同步,会触发额外加载逻辑。 - 无请求锁机制:滚动触发loadMore后,请求完成前可能重复触发loadMore,导致短时间内大量API调用。
- 内容高度不足触发无限加载:如果渲染的列表总高度没填满滚动容器,
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
相关产品推荐
相关产品推荐

