【问题标题】:Next.js router is returning query parameters as undefined on first render?Next.js 路由器在第一次渲染时返回未定义的查询参数?
【发布时间】:2021-07-23 16:35:59
【问题描述】:

我的 Next.js 应用中有一个页面,它执行以下操作:

  • 这是一个搜索页面
  • 路径名是这样的:/search?q=search+slug
  • 在客户端加载数据
  • 需要读取router.query才能得到router.query.q的值

PS:我正在使用 Redux

  const dispatch = useDispatch();
  const router = useRouter();
  const query = router.query as { q: string };
  const queryString = query.q;

  console.log("SEARCH CONTAINER");
  console.log(`queryString: ${queryString}`);

  useEffect(() => {
    dispatch(THUNK.LOAD(queryString));
    return () => { dispatch(ACTION.RESET_STATE()); };
  },[dispatch,queryString]);

请参阅useEffect。理论上每个queryString(实际上是req.query.q)应该只运行一次。

但我被重复了THUNK.LOAD 操作。这就是为什么我在那里添加了console.log()

这就是它正在注销的内容:

然后:

这就是我收到重复调度的原因。当然,我可以在发送之前检查if (queryString),或者我可以从window.location.search 获得它。但我很惊讶router.query.q 首先是undefined。这怎么可能呢?为什么req.query 对象会被异步填充?对此有何解释?

【问题讨论】:

    标签: reactjs next.js query-string


    【解决方案1】:

    刚刚发现我的问题的解决方案是什么:

    发件人:https://nextjs.org/docs/api-reference/next/router#router-object

    isReady:boolean - 路由器字段是否更新 客户端并准备好使用。只能在内部使用 useEffect 方法,不适用于在服务器上进行条件渲染。

    当您点击/search?q=XXX 时,客户端上的router.query 会发生这种情况。

    第一次渲染

    router.isReady: false
    router.query: {}
    

    后续渲染

    router.isReady: true
    router.query: {"q":"xxx"}
    

    结论

    router.query 在第一次运行时未填充到客户端(对于 SSG 页面)这一事实是设计实现细节。您可以通过检查 router.isReady 布尔属性来监控它是否已被填充。

    【讨论】:

      【解决方案2】:

      这是因为 nextjs 在服务器端和客户端都工作,因此有必要通过执行typeof window !== 'undefined' 之类的操作来检查您实际上是在服务器端还是客户端上,因为该组件需要在客户端进行水合。

      当你使用 useRouter 钩子时,它只在客户端运行,原因很明显,但是当 nextjs 将“组件”传递给浏览器时,它不知道 router.query.q 指的是什么,只有当组件在客户端获得水分,反应钩子启动,然后您可以检索queryString

      这也类似于您需要使用dynamic 导入的原因 大多数客户端库的 nextjs 应用程序。

      附:我自己是新的 nextjs,但我希望这是有道理的。

      【讨论】:

      • 谢谢。你确定这是原因吗?这有点道理,但也很奇怪为什么你需要额外运行 useRouter 钩子。你为什么不在第一次运行时立即检查window
      • 我正在进一步调查。我认为你的答案是正确的。看来 Next.js 最初是从 window.__NEXT_DATA__ 获取 query 对象,这是来自服务器的预渲染数据。如果你没有在服务器上填充它(可能通过使用动态路由或使用getServerProps),router.query obj 在第一次渲染时将为空。它只会在稍后的渲染周期中从您的浏览器读取查询。我不知道它是什么时候发生的,但它似乎是自动发生的。我也在问它here
      【解决方案3】:

      基于@cbdeveloper 的回答,这里是运行 NextJS 的示例代码,用于在客户端渲染期间获取路由器数据:

      文件名:/pages/[...results].tsx

      import { useRouter } from 'next/router';
      import { useEffect, useState } from 'react';
      
      export default function Results() {
        const router = useRouter();
        const { q } = router.query;
      
        const [searchQuery, setSearchQuery] = useState("");
      
        useEffect(() => {
          console.log(`useEffect triggered`);
          router.isReady ? setSearchQuery(q as string) : console.log('router is not ready');
        },[q]);
      
        return (
          <>
            <p>q: {router.query.q}</p>
            <p>searchQuery: {searchQuery}</p>
            <p>bar: {router.query.bar}</p>
      
            <form>
              <label>Search Query:</label>
              <input
                type="text"
                name="q"
                value={searchQuery}
                onChange={(e) => setSearchQuery(e.target.value)}
              />
              <button type="submit">Submit</button>
            </form>
          </>
        );
      }
      

      【讨论】:

      • 我最终遇到了这个问题,因为我一直在尝试使用 catch all routes (pages/[...slug].js) 来调试路由。从 pages/index.js 导航到 [...slug].js 页面时,一切正常。但是当我尝试从 [...slug].js 导航到另一个 [...slug].js 页面(例如 /careers 到 /blog)时,只有 URL 会发生变化,但不会发生补液或重新渲染,你有任何可能的解决方案?
      • 上面这个例子不适用于我使用下一个 12.1.0。出于某种原因,useEffect 挂钩仅在说“路由器尚未准备好”时触发,仅此而已。
      【解决方案4】:

      This solution 为我工作:

      const router = useRouter();
      React.useEffect(() => {
        if (router.isReady) {
          // Code using query
          console.log(router.query);
         }
      }, [router.isReady]);
      

      【讨论】:

        猜你喜欢
        • 2023-01-14
        • 1970-01-01
        • 2020-07-17
        • 2022-01-23
        • 1970-01-01
        • 2016-07-11
        • 1970-01-01
        • 2017-02-09
        • 2022-07-14
        相关资源
        最近更新 更多