基于FakeStoreAPI的无限滚动应用:滚动底部数据更新异常排查
无限滚动应用问题分析与修复方案
问题成因
1. 重复加载多项
- 滚动事件触发时未检查
loading状态,滚动到底部的判定逻辑会连续触发多次handleScroll,导致同一时间发起多个重复请求。 handleScroll中先调用fetchData再设置loading=true,顺序错误,无法阻止后续重复触发。
2. UI不渲染新数据
useEffect依赖为空数组,handleScroll和fetchData捕获的是组件初始化时的page值(始终为1),导致每次请求都获取第一页数据,即使state在累加,也只是重复添加相同内容,且page无法正确递增。- 未清理滚动事件监听,可能引发内存泄漏,也会影响组件卸载后的异常事件触发。
修改方案
步骤1:修复闭包与状态捕获问题
用useCallback包裹handleScroll,确保函数能捕获最新的page和loading状态;同时在useEffect中添加对应依赖,保证事件监听能更新到最新的函数。
步骤2:阻止重复请求
在handleScroll中先判断loading状态,只有未加载时才发起请求,且先设置loading=true再调用fetchData,避免重复触发。
步骤3:添加事件清理
在useEffect的返回函数中移除滚动事件监听,避免内存泄漏。
修改后的完整代码
import { useState, useEffect, useCallback } from "react"; const PAGE_NUMBER = 1; function App() { const [state, setState] = useState([]); const [page, setPage] = useState(PAGE_NUMBER); const [loading, setLoading] = useState(false); const fetchData = async () => { try { const res = await fetch( `https://fakestoreapi.com/products?limit=5&page=${page}` ); const data = await res.json(); setState((prevData) => [...prevData, ...data]); setPage((prevPage) => prevPage + 1); } catch (err) { console.error('数据加载失败:', err); } finally { setLoading(false); } }; const handleScroll = useCallback((e) => { if (loading) return; if ( window.innerHeight + e.target.documentElement.scrollTop + 1 >= e.target.documentElement.offsetHeight ) { setLoading(true); fetchData(); } }, [loading, page]); useEffect(() => { fetchData(); window.addEventListener("scroll", handleScroll); return () => { window.removeEventListener("scroll", handleScroll); }; }, [handleScroll]); return ( <div className="App"> <h1>无限滚动示例</h1> <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: '1rem' }}> {state.map((product) => ( <div key={product.id} style={{ border: '1px solid #eee', padding: '1rem', borderRadius: '8px' }}> <img src={product.image} alt={product.title} style={{ width: '100%', height: '150px', objectFit: 'contain' }} /> <h3 style={{ fontSize: '0.9rem', marginTop: '0.5rem' }}>{product.title}</h3> </div> ))} </div> {loading && <p style={{ textAlign: 'center', padding: '1rem' }}>加载中...</p>} </div> ); } export default App;
额外优化点
- 增加了请求错误捕获,避免请求失败导致
loading一直处于true状态。 - 添加了加载状态提示,提升用户体验。
- 优化了列表项布局样式,让展示更规整。
内容的提问来源于stack exchange,提问作者Pranav
相关产品推荐
相关产品推荐

