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

如何正确缓存useEffect依赖项?解决路由滚动的依赖警告问题

解决方案:用useRef标记首次滚动状态

你可以通过useRef创建一个标记变量,用来记录是否已经完成过首次滚动操作,这样既不需要依赖firstVisit.app,也能满足首次加载不等待动画、后续路由切换等待动画的需求:

import { useRef, useEffect } from 'react';

// 组件内部
const hasDoneFirstScroll = useRef(false);

useEffect(() => {
    // 判断是否是首次滚动
    const delay = hasDoneFirstScroll.current ? pageTransitionTime * 1000 : 0;
    
    const timer = setTimeout(() => {
        window.scrollTo(0, 0);
    }, delay);

    // 首次滚动后标记为已完成
    if (!hasDoneFirstScroll.current) {
        hasDoneFirstScroll.current = true;
    }

    return () => clearTimeout(timer);
}, [location.pathname, pageTransitionTime]);

逻辑说明:

  • hasDoneFirstScroll是useRef变量,它的.current属性不会触发组件重渲染,也不会被useEffect的依赖数组监听。
  • 第一次触发useEffect(对应首次路由加载)时,hasDoneFirstScroll.current为false,延迟设为0,立即滚动到顶部,之后将标记设为true。
  • 后续路由切换时,location.pathname变化触发useEffect,此时标记已经是true,会使用页面过渡动画的时长作为延迟,等待动画完成后再滚动。

备选方案:缓存初始的firstVisit.app值

如果你想继续依赖firstVisit.app的初始状态,可以用useRef缓存它的初始值,避免将其加入useEffect的依赖数组:

import { useRef, useEffect } from 'react';

// 组件内部
const initialFirstVisit = useRef(firstVisit.app);

useEffect(() => {
    const delay = initialFirstVisit.current ? 0 : pageTransitionTime * 1000;
    
    const timer = setTimeout(() => {
        window.scrollTo(0, 0);
    }, delay);

    return () => clearTimeout(timer);
}, [location.pathname, pageTransitionTime]);

逻辑说明:

  • initialFirstVisit在组件挂载时就缓存了firstVisit.app的初始值(也就是首次加载时的true)。
  • 后续即使firstVisit.app被你的另一个useEffect改为false,initialFirstVisit.current依然保留初始的true,不会影响useEffect的判断逻辑。
  • 这个方案不需要修改你原本的Redux状态逻辑,只是通过useRef隔离了初始值的依赖。

为什么不直接把firstVisit.app加入依赖?

如果将firstVisit.app加入useEffect的依赖数组,当你的另一个useEffect把它从true改为false时,会再次触发这个滚动逻辑的useEffect,此时会设置一个pageTransitionTime * 1000的延迟定时器,导致页面在首次加载后又自动滚动一次,破坏用户体验。而上面的两种方案都避免了这个问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 03:05:23