Next.js路由切换后React State无法更新,需硬刷新问题求助
React State 无法更新导致无限滚动路由切换后失效(Next 14.1.4 + React 18)
问题现象
- 开发客户端无限滚动组件时,路由切换后State出现“卡住”,无法触发滚动加载,必须硬刷新才能恢复
- 仅当用户滚动到底部后切换路由,组件才能正常工作;中途切换路由返回则失效
- 尝试
useState+useEffect初始化数据、调用router.refresh()均无法解决
核心原因
- 全局变量状态污染:全局的
chunk变量不会随组件卸载/重新挂载重置,Next.js客户端导航保留组件实例后,旧的chunk.number值会导致滚动判断逻辑失效 - 闭包捕获旧值:
onScroll函数未用useCallback包裹,每次渲染生成新函数,且捕获的data、chunk为旧值,状态更新逻辑异常 - 事件监听清理不彻底:组件卸载时未移除滚动监听,且变量拼写错误(
halfOfTheBlockheight应为halfOfTheBlockHeight)导致判断逻辑出错
修复后的代码
'use client' import Link from 'next/link'; import { useState, useEffect, useCallback } from 'react'; export default function TodoList() { // 初始化基础数据 const initialTodos: { id: number, text: string }[] = []; for (let i = 0; i < 30; i++) { initialTodos.push({ id: i, text: "some fetched todos #" + (i + 1) }); } const [data, setState] = useState(initialTodos); // 将全局chunk改为组件内state,确保每次挂载重置 const [chunkNumber, setChunkNumber] = useState(1); // 使用useCallback包裹,确保捕获最新state,避免重复创建函数 const onScroll = useCallback((event: Event) => { const vp = event.target as VisualViewport; const itemHeight = 148; const itemsPerChunk = 30; const halfOfTheBlockHeight = (itemHeight * itemsPerChunk) / 2; const totalHeightOfTheBlock = itemHeight * itemsPerChunk * chunkNumber; // 修正拼写错误 const middleOfTheBlockPageTop = totalHeightOfTheBlock - halfOfTheBlockHeight; if (vp.pageTop > middleOfTheBlockPageTop) { const newChunkNumber = chunkNumber + 1; setChunkNumber(newChunkNumber); // 生成新数据并更新state const length = data.length; const newArr = [...data]; for (let i = length; i < length + 30; i++) { newArr.push({ id: i + 1, text: 'random' + i }); } setState(newArr); } }, [data, chunkNumber]); useEffect(() => { const vp = window.visualViewport; if (!vp) return; vp.addEventListener('scroll', onScroll); // 组件卸载时移除监听,避免内存泄漏 return () => { vp.removeEventListener('scroll', onScroll); }; }, [onScroll]); return ( <> <Link href="/"> Base Route </Link> <Link href="/example"> Another Route </Link> <ul> {data.map((item: { id: number, text: string }) => ( <li style={{ height: 148 }} key={item.id}> {item.text} </li> ))} </ul> </> ); }
修复要点
- 把全局
chunk变量替换为组件内的useState,确保组件每次挂载时chunkNumber重置为1 - 用
useCallback包裹onScroll,保证函数引用稳定,且能捕获最新的data和chunkNumber值 - 在
useEffect中添加清理函数,组件卸载时移除滚动监听,防止内存泄漏 - 修正变量拼写错误,确保滚动判断逻辑准确
内容的提问来源于stack exchange,提问作者George
相关产品推荐
相关产品推荐

