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

Next.js中用useState、useEffect每8秒更新滚动位置状态不更新如何解决?

问题原因
  • 闭包陷阱:你编写的useEffect依赖数组为空,仅会在组件首次渲染时执行一次,内部绑定的lazyProgressUpdate函数始终引用首次渲染时的状态值(也就是lazyProgress和lazyProgressMax的初始值0),后续状态更新不会同步到这个闭包内的函数里。
  • useState异步更新特性:调用setLazyProgress(scroll)后,React不会立刻修改当前作用域下的lazyProgress变量,所以后续判断lazyProgress > lazyProgressMax时拿到的还是更新前的旧值,自然不会触发lazyProgressMax的更新。
  • 额外风险:Next.js 存在服务端渲染(SSR)阶段,服务端环境不存在document对象,直接调用会触发报错。
修复方案

直接用计算得到的实时scroll值做判断,同时用函数式状态更新避免依赖闭包旧值,再补充SSR兼容逻辑即可,修改后代码如下:

import { useState, useEffect } from 'react';

const [lazyProgress, setLazyProgress] = useState(0);
const [lazyProgressMax, setLazyProgressMax] = useState(0);

function lazyProgressUpdate() {
  // 兼容SSR场景,服务端无document直接返回
  if (typeof document === 'undefined') return;
  const scrollTotal = document.documentElement.scrollTop;
  const heightWin = document.documentElement.scrollHeight - document.documentElement.clientHeight;
  // 避免页面内容不满一屏时分母为0报错
  if (heightWin === 0) return;
  const scroll = scrollTotal / heightWin * 100;

  setLazyProgress(scroll);
  // 函数式更新直接拿最新的历史最大值做比较,不依赖闭包旧值
  setLazyProgressMax(prevMax => Math.max(prevMax, scroll));
}

useEffect(() => {
  const timer = setInterval(() => {
    lazyProgressUpdate();
  }, 8000);
  return () => clearInterval(timer);
// 补充依赖确保函数引用更新
}, [lazyProgressUpdate]);

// 若需要打印更新后的状态,单独监听状态变化即可
useEffect(() => {
  console.log(lazyProgress, lazyProgressMax);
}, [lazyProgress, lazyProgressMax]);
可选优化

如果需要滚动时实时更新进度,不需要固定8秒轮询,可以把定时器替换为scroll事件监听,配合节流函数避免高频触发性能问题。

内容的提问来源于stack exchange,提问作者Aadhit Shanmugam

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 19:36:01