ReactJs/NextJS登录欢迎页5秒自动跳转 状态更新异常如何解决
问题根因
这是React Hook的经典闭包陷阱问题:你给useEffect传入的依赖数组为空,因此useEffect只会在组件首次挂载时执行一次,内部的setInterval回调捕获的是首次渲染时的变量快照,其中redirectSeconds永远是初始值5,所以每次执行setRedirectSeconds(redirectSeconds - 1)都只会把状态设为4,自然不会继续倒计时。
修复方案
可以通过setState的函数式更新语法拿到最新的状态值,同时补充定时器清理逻辑避免内存泄漏,另外你原来代码外层的setTimeout会让倒计时晚1秒启动,总停留时间变成6秒,如果不需要可以直接去掉。
修复后代码如下:
const Welcome: NextPage = (): ReactElement => { const [redirectSeconds, setRedirectSeconds] = useState<number>(5); const router = useRouter(); const query = router.query; useEffect(() => { if (!query?.redirect) return; // 启动倒计时定时器 const interval = setInterval(() => { setRedirectSeconds(prev => { // 用函数式更新可以拿到上一次的最新状态,不受闭包影响 if (prev <= 1) { clearInterval(interval); router.push(query.redirect.toString()); return 0; } return prev - 1; }); }, 1000); // 组件卸载时清理定时器,避免内存泄漏 return () => clearInterval(interval); }, [query, router]); // 把用到的外部变量加入依赖数组,符合eslint规则 return ( <div> Welcome, you have successfully logged in, you will be redirected in.. {redirectSeconds} </div> ); }; export default Welcome;
补充说明
如果需要保留原来的「先等1秒再开始倒计时」的逻辑,只需要把setInterval再套回原来的setTimeout里即可,注意要同时把setTimeout的返回值也加入清理逻辑:
useEffect(() => { if (!query?.redirect) return; let interval: NodeJS.Timeout; const timer = setTimeout(() => { interval = setInterval(() => { setRedirectSeconds(prev => { if (prev <= 1) { clearInterval(interval); router.push(query.redirect.toString()); return 0; } return prev - 1; }); }, 1000); }, 1000); // 清理所有定时器 return () => { clearTimeout(timer); if (interval) clearInterval(interval); }; }, [query, router]);
内容的提问来源于stack exchange,提问作者Denis2310
相关产品推荐
相关产品推荐

