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

Next.js中useEffect调用router.push引发无限循环与ESLint告警

问题根因

ESLint的react-hooks/exhaustive-deps规则报警,是因为检测到effect内部使用了router变量但未加入依赖数组。加了router就无限循环的原因很简单:Next.js Pages Router里useRouter()返回的router对象不是稳定引用,每次路由跳转完成后,这个对象的内存地址都会变。
触发死循环的完整链路:

  1. 防抖搜索词更新 → 触发effect → 执行router.push更新URL
  2. 路由更新完成 → router引用地址变化 → 依赖数组检测到变更 → 再次触发effect
  3. effect再次执行router.push(哪怕跳转路径和当前完全一致)→ 再次触发路由更新 → 死循环
直接跳过依赖检查是否可行

非常不推荐直接加eslint-disable注释绕过规则:

  • 这种写法属于治标不治本,本质是隐瞒了闭包风险。如果后续你在这个effect里新增读取router其他属性的逻辑,很容易因为闭包拿到旧的router值,出很难排查的bug。
  • 除非你完全确定这个effect后续不会再修改,且用到的router.push逻辑永远不会变,才可以临时用这种方式兜底。
推荐解决方法

方案1:加前置判断(兼容所有Pages Router版本,零额外依赖)

在effect内部先判断当前URL的q参数是不是已经和防抖搜索词一致,一致就直接return不执行跳转,从根源切断循环。这种写法可以正常把router加入依赖数组,完全符合ESLint要求:

useEffect(() => {
  // 目标搜索词和当前URL参数一致时不重复跳转,直接阻断循环
  if (router.query.q === debouncedSearchTerm) return;
  // 记得给搜索词做URL编码,避免特殊字符(&、=、空格、中文等)破坏query结构
  const newPath = `/search?q=${encodeURIComponent(debouncedSearchTerm)}`;
  router.push(newPath, undefined, { shallow: true });
}, [debouncedSearchTerm, router]);

哪怕router的引用变了触发effect,只要参数已经对了就不会执行push,自然不会触发新的路由更新,死循环直接就断了。

方案2:迁移到App Router(Next.js 13+ 适用)

如果项目用的是Next.js 13及以上版本的App Router,从next/navigation导入的useRouter返回的是稳定引用,不会随路由跳转改变,直接把router加进依赖数组也不会触发无限循环,不需要额外做特殊处理。

其他可优化的代码问题

你贴的代码里还有几个小问题可以一起修:

  • 结果赋值那行setSearchResults(data)l末尾多了个多余的l,会触发语法错误
  • 拉取搜索结果的effect可以增加空值判断,当router.query.q为空的时候提前返回,不要发无效请求
  • 输入框组件记得补全onChange的类型定义,避免TS报错

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 14:51:17