useRouter/withRouter 在第一次渲染时接收未定义的查询

Prz*_* eS 14 next.js

我的动态路线有问题。它看起来像这样

[lang]/abc

我正在尝试从中获取查询值,[lang]但是当我使用时,我useRouter/withRouter在页面的 2-3 渲染期间得到了查询(首先我得到了query.lang = undefined)。是否可以进行 1 次渲染或使用任何技术?

在此处输入图片说明

小智 24

我发现了一些东西:

isReady: boolean - 路由器字段是否在客户端更新并准备好使用。应该只在 useEffect 方法内部使用,而不是在服务器上有条件地呈现。 https://nextjs.org/docs/api-reference/next/router#router-object

代码如下:

const router = useRouter();
useEffect(()=>{
    if(!router.isReady) return;

    // codes using router.query

}, [router.isReady]);
Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案。这正是引入 isReady 的目的。 (5认同)

Nik*_*lev 22

在第一次渲染时无法获得查询值。

静态优化页面在没有提供路由参数的情况下被水合。例如query是一个空对象 ( {})。

水化后,Next.js 将填充查询对象。

Next.js 10.0.5 及更高版本

使用router.isReady内部useEffect钩子检查参数是否准备就绪。有关示例,请参阅 @dy063 的答案。

Next.js 10.0.5 之前

首先渲染一个动态路由router.asPath并且router.route是相等的。一旦query对象可用,就router.asPath反映它。

您可以在更改useEffect后依赖挂钩内的查询值asPath

const router = useRouter();

useEffect(() => {
  if (router.asPath !== router.route) {
    // router.query.lang is defined
  }
}, [router])
Run Code Online (Sandbox Code Playgroud)

GitHub 问题 - 向“useRouter”返回的路由器添加“就绪”


Hun*_*ney 11

在 NextJS 9+ 中,确保路由参数可立即用于页面组件的一种方法是从context传递给的arg 中获取它们getServerSideProps()并作为 props 传递给组件。

对于像这样的页面[id].js

export function getServerSideProps(context) {
  return {
    props: {params: context.params}
  };
}

export default ({params}) => {
  const {id} = params;
  return <div>You opened page with {id}</div>;
};
Run Code Online (Sandbox Code Playgroud)

  • 使用“getServerSideProps”指示 Next.js 根据每个请求在服务器端预渲染页面。意味着它没有静态优化和缓存。仅当您愿意牺牲性能时才使用它。例如,如果您必须在首次渲染之前获取数据。 (3认同)

Ale*_*lop 9

这是一个很好的问题,我花了几天时间才弄清楚最好的方法是什么。

我个人找到了三种可行的解决方案来解决验证动态路径路径参数甚至只是一般路径路径参数的问题。

三个解决方案是

  1. SSR(不推荐)[下一个 >= 10]
  2. 使用路由器
  3. 中间件 [需要接下来 12 个]

在我的示例中, a 将使用需要重置令牌的路由,否则应该重定向。

SSR 首先是服务器端渲染getServerSideProps。Vercel 建议使用 SSR 作为最后的手段,我强烈建议在可能的情况下不要使用 SSR(字节时间和成本)。

我们建议尝试增量静态生成或客户端获取,看看它们是否满足您的需求。 https://vercel.com/blog/nextjs-server-side-rendering-vs-static- Generation

但在这种情况下,假设您需要一些服务器端 api 验证调用来验证查询参数。

export const getServerSideProps = async (context) => {
  const { token } = context.query;

  if (!token) {
    return {
      redirect: {
        permanent: false,
        destination: "/",
      }
    }
  }

  return {
    props: {}
    // props: { token }
    // You could do this either with useRouter or passing props
  }
}
Run Code Online (Sandbox Code Playgroud)

useRouter其次是最简单的useRouter。当我第一次这样做时,我遇到了一个问题,当 nextjs/react 水合时,查询参数将为空。幸运的是 useRouter 已经准备好了!

import Router, { useRouter } from "next/router";

const { query, isReady } = useRouter();

useEffect(() => {
  if (!isReady) return;
  if (!query.token) {
    Router.push("/")
  }
}, [isReady])
Run Code Online (Sandbox Code Playgroud)

中间件现在是我个人最喜欢的,因为它以一种干净的方式分离了功能。我发现这是基于一个 vercel 示例。我强烈建议您阅读其中的一些内容以找到最佳实践。 https://github.com/vercel/examples/

import { NextResponse, NextRequest } from 'next/server'

export async function middleware(req) {
    const { pathname, searchParams } = req.nextUrl

    if (pathname == '/reset-token') {
        const index = searchParams.findIndex(x => x.key === "token")
        // You could also add token validation here.
        if (!index) {
          return NextResponse.redirect('/')
        }
    }
    return NextResponse.next()
}
Run Code Online (Sandbox Code Playgroud)

这是一个对查询参数进行一些很酷的过滤的存储库。这是一种更软的方法,而不是硬重定向。 https://github.com/vercel/examples/tree/main/edge-functions/query-params-filter

Nico 对此也有一个很好的答案,希望我不会建议像他的示例中那样使用 hooks,而是使用isReady. /sf/answers/4072787491/