Next.js无限滚动首次触发加载时重复渲染最后一条元素问题
问题原因
三个核心问题导致首次加载重复:
- 分页计数器初始值错误:服务端
getServerSideProps已经请求了start=0, limit=10的第一页10条数据,但客户端pageCounter初始值默认为0,第一次触发无限滚动的加载方法时,传入的分页起点还是0,会重复请求第一页数据,和已有列表拼接后就会出现重复内容。第一次请求结束后pageCounter被更新为10,后续分页就会正常,所以异常仅在首次触发时出现。 - 列表key设置错误:渲染文章卡片时使用
newKey()生成随机值作为key,React每次重渲染都会生成全新的随机key,无法正确识别DOM节点对应关系,会出现节点复用错乱、内容重复渲染的问题。 - 缺少初始加载控制:
react-infinite-scroller默认会在组件挂载时自动触发一次loadMore,和首屏服务端加载逻辑冲突,容易触发重复请求。
修复方案
按以下步骤调整代码即可解决问题:
- 修正分页计数器初始值:首屏已经加载10条数据,客户端初始化
pageCounter时直接设为10,同时初始化hasMore状态判断首屏是否已经加载完全部数据。 - 给
InfiniteScroll组件添加initialLoad={false}属性,禁止组件初始化时自动触发加载,首屏数据完全由服务端渲染提供。 - 添加请求锁,防止滚动事件高频触发导致重复请求。
- 替换列表渲染的key值,使用Strapi返回的文章唯一id作为key,保证同一条数据的key固定,避免React渲染错位。
修复后核心代码
状态初始化
// posts初始值直接用服务端返回的articles const [posts, setPosts] = useState(articles) const [pageCounter, setPageCounter] = useState(10) const [hasMore, setHasMore] = useState(total > 10) const [loading, setLoading] = useState(false)
加载更多方法
const getMorePost = async () => { if (loading || pageCounter >= total) { setHasMore(pageCounter < total) return } setLoading(true) try { const res = await getArticles({locale}, cookieJwt, pageCounter, 10) const newPosts = res.articlesList setPosts(prev => [...prev, ...newPosts]) setPageCounter(prev => prev + 10) setHasMore(pageCounter + 10 < total) } catch (e) { console.error('加载文章失败', e) } finally { setLoading(false) } }
无限滚动组件调整
<InfiniteScroll pageStart={0} loadMore={getMorePost} hasMore={hasMore} initialLoad={false} loader={<div className={cardStyle.loaderWrapper}><div className={cardStyle.loader}><Loader color={'orange'} variant={'bars'} /></div></div>} > <SimpleGrid cols={3} spacing="xl" breakpoints={[ { maxWidth: 1200, cols: 2, spacing: 'md' }, { maxWidth: 755, cols: 1, spacing: 'sm' }, ]} > {posts.map((article) => ( <ArticleCard key={article.id} data={article} locale={locale} onClick={() => handleClick(article)} /> ))} </SimpleGrid> </InfiniteScroll>
内容的提问来源于stack exchange,提问作者altho
相关产品推荐
相关产品推荐

