Next.js 路由器在第一次渲染时返回未定义的查询参数?

Next.js router is returning query parameters as undefined on first render?

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

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 对象?对此有何解释?

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

当你使用 useRouter 挂钩时,由于显而易见的原因,它只在客户端运行,但是当 nextjs 将“组件”传递给浏览器时,它不知道 router.query.q 指的是什么,只有当组件在客户端水合时,反应挂钩才会启动,然后您可以检索 queryString

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

P.S。我自己是 nextjs 的新手,但我希望这是有道理的。

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

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

isReady: boolean - Whether the router fields are updated client-side and ready for use. Should only be used inside of useEffect methods and not for conditionally rendering on the server.

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

第一次渲染

router.isReady: false
router.query: {}

后续渲染

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

结论

第一个 运行 客户端(对于 SSG 页面)未填充 router.query 这一事实是一个设计实现细节。您可以通过检查 router.isReady 布尔值 属性.

来监控它是否已被填充

基于@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>
    </>
  );
}

This solution 为我工作:

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