如何正确缓存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
相关产品推荐
相关产品推荐

