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

Next.js路由切换后React State无法更新,需硬刷新问题求助

React State 无法更新导致无限滚动路由切换后失效(Next 14.1.4 + React 18)

问题现象

  • 开发客户端无限滚动组件时,路由切换后State出现“卡住”,无法触发滚动加载,必须硬刷新才能恢复
  • 仅当用户滚动到底部后切换路由,组件才能正常工作;中途切换路由返回则失效
  • 尝试useState+useEffect初始化数据、调用router.refresh()均无法解决

核心原因

  1. 全局变量状态污染:全局的chunk变量不会随组件卸载/重新挂载重置,Next.js客户端导航保留组件实例后,旧的chunk.number值会导致滚动判断逻辑失效
  2. 闭包捕获旧值:onScroll函数未用useCallback包裹,每次渲染生成新函数,且捕获的data、chunk为旧值,状态更新逻辑异常
  3. 事件监听清理不彻底:组件卸载时未移除滚动监听,且变量拼写错误(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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 03:05:15