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

Next.js中如何实现仅在router.query.id变化时触发且可调用router.push的useEffect

Solution for Stable Router Push in Next.js useEffect

Great question! The core issue here is that Next.js's useRouter hook returns a fresh router object every time any part of the route changes—including query parameters like page. This means the push method’s reference updates on every route tweak, causing your useEffect to re-run even when id stays the same.

Best Approach: Use useRef to Hold a Stable Router Reference

The cleanest way to avoid this is to store the router instance in a ref, which maintains a stable reference across re-renders. This lets you call push safely without adding it to your useEffect’s dependency array.

Here's how to implement it:

import { useRouter, useEffect, useRef } from 'next/router';

const YourComponent = () => {
  const router = useRouter();
  const routerRef = useRef(router);
  const { id } = router.query;

  // Keep the ref updated with the latest router instance
  useEffect(() => {
    routerRef.current = router;
  }, [router]);

  useEffect(() => {
    // Guard clause: Exit if id is undefined (common during hydration)
    if (!id) return;

    try {
      fetchDataFor(id);
    } catch (err) {
      // Use the stable router reference to push
      routerRef.current.push('/not-found');
    }
  }, [id]); // Only re-run when id changes—perfect!

  // ... rest of your component
};

Why This Works

  • The routerRef holds the latest router instance at all times, thanks to the small useEffect that updates it whenever router changes.
  • Your main useEffect only depends on id, so it will only trigger when id changes—exactly what you need.
  • You avoid the anti-pattern of ignoring dependencies with eslint-disable-next-line, which can lead to stale closures and hard-to-debug bugs later.

Why Other Options Fall Short

  1. Removing push from dependencies: This is an anti-pattern because it can cause your useEffect to use a stale push reference, leading to unexpected behavior if the router state changes.
  2. Dynamic routes: Even if you switch to /some-route/[id], changing the page query param will still create a new router object, so push’s reference will update and your useEffect will re-run unnecessarily.
  3. Wrapping push in useCallback: This doesn’t solve the problem because the callback’s dependency on router means it will still update every time the route changes, triggering the useEffect.

Bonus: Handling Hydration Edge Cases

Note the guard clause if (!id) return—this is important because Next.js hydrates components on the client, and router.query.id might be undefined briefly during that process. Adding this prevents unnecessary errors or fetch calls.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.01 02:47:42