next.js useRouter/withRouter在第一次呈现查询时接收未定义

wlzqhblo  于 2022-12-18  发布在  其他
关注(0)|答案(9)|浏览(139)

我的动态路线出现问题。它看起来像这样
[lang]/abc
我试图从[lang]获取查询值,但当我使用useRouter/withRouter时,我在页面的2-3渲染期间得到了查询(第一次我得到了query.lang = undefined)。有可能得到1渲染或使用任何技术吗?

vd2z7a6w

vd2z7a6w1#

我发现了一些东西:
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]);
t5fffqht

t5fffqht2#

不可能在第一次呈现时获取查询值。
静态优化页在未提供路由参数的情况下水合。例如,query是空对象({})。
水合之后,Next.js将填充查询对象。

Next.js 10.0.5及更高版本

useEffect钩子中使用router.isReady来检查参数是否准备好了。请参见@doxylee的回答。

在下一个. js 10.0.5之前

动态路由的第一次渲染router.asPathrouter.route相等。一旦query对象可用,router.asPath就反映它。
asPath更改后,您可以依赖useEffect钩子内的查询值。

const router = useRouter();

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

GitHub Issue - Add a "ready" to Router returned by "useRouter"

b5buobof

b5buobof3#

在NextJS9+中,确保路由参数立即可用于页面组件的一种方法是从传递给getServerSideProps()context参数中获取这些参数,并将其作为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>;
};
8mmmxcuj

8mmmxcuj4#

这是一个很棒的问题,我花了几天时间才弄清楚最好的方法是什么。
对于验证动态路由路径参数或一般路由路径参数的问题,我个人找到了三种可行的解决方案。
这三种解决方案是

  1. SSR(不推荐)[Next〉= 10]
    1.使用路由器
    1.中间件[下12个必填项]
    在我的例子中,a将使用一个需要reset-token的路由,否则它应该被重定向。

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
  }
}

useRouter第二个最简单的useRouter。当我第一次这样做的时候,我遇到了一个问题,当nextjs/react水合时,会有一个查询参数为空的点。幸运的是useRouter有isReady!

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

const { query, isReady } = useRouter();

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

中间件现在这是我个人的最爱,因为它以一种干净的方式分离了功能。我发现这是基于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()
}


下面是一个repo,它对查询参数进行了一些很酷的过滤。这是一个更软的方法,而不是硬重定向。https://github.com/vercel/examples/tree/main/edge-functions/query-params-filter
Nico对此也有一个很好的答案,但我不建议像他的例子那样使用钩子,而是使用isReady. https://stackoverflow.com/a/58182678/4918639

vwoqyblh

vwoqyblh5#

对于类组件爱好者

更好的方法是使用this.props.router.events.on方法监听routeChangeComplete的专用事件,如果使用类component -,则在componentDidMount中监听该事件

routeChangeComplete = () => {
    // this WILL have valid query data not empty {}
    console.log(this.props.router.query);
};
componentDidMount() {
    this.props.router.events.on("routeChangeComplete", this.routeChangeComplete);
}
componentWillUnmount() {
    this.props.router.events.off("routeChangeComplete", this.routeChangeComplete);
}

参考:https://nextjs.org/docs/api-reference/next/router#routerevents
路线变更完成:在路线完全更改时激发。
实际上,当isReady变为true时,或者当router.query对象具有数据时。

iqih9akk

iqih9akk6#

适用于NextJS版本-12.0.8
如果从页面导出名为getServerSideProps(服务器端呈现)的函数,则Next.js将使用getServerSideProps返回的数据在每次请求时预呈现此页面。
=异步函数
参考:https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props#getserversideprops
简单地把这个异步函数放在页面上就可以通知NextJS它的存在。在组件的预呈现阶段,路由器的查询对象将为空。
isReady:boolean -路由器字段是否在客户端更新并准备好使用。应该只在useEffect方法内部使用,而不是在服务器上有条件地呈现。
参考:https://nextjs.org/docs/api-reference/next/router
解决方案:

import { useRouter } from 'next/router';

const Fn = () =>{

const router = useRouter();
const { param } = router.query;

const fetchData = async () => {
   await fetch();
} 

 useEffect(() => {
   fetchCat();
}, [router.isReady]);

}
jgovgodb

jgovgodb7#

我解决了我的问题,我需要它在特殊组件。我 Package 使用withRouter(withLocale(Comp))和创建条件在HOC中

export default function withLocale(WrappedPage) {
    const WithLocale = ({ router, ...props }) => {
        const { lang } = router.query;
        if (!lang || !isLocale(lang)) {
            return <Error statusCode={404} />;
        }
        return (
            <LocaleProvider lang={lang}>
                <WrappedPage {...props} />
            </LocaleProvider>
        );
    };
   return WithLocale;
}
r1zhe5dt

r1zhe5dt8#

下一个. js〈= 10.0.5

这是一个很好的工作周围,我发现周围从这个评论

添加useQuery.ts帮助文件

// useQuery.js
import { useRouter } from 'next/router';

// Resolves query or returns null
export default function useQuery() {
  const router = useRouter();
  const ready = router.asPath !== router.route;
  if (!ready) return null;
  return router.query;
}

用法

// In your components (instead of useRouter)
const query = useQuery();

useEffect(() => {
  if (!query) {
    return;
  }
  console.log('my query exists!!', query);
}, [query]);
sczxawaw

sczxawaw9#

类组件|2022年12月16日|ReactJS 18.2.0|Next.js 13.0.6

对于那些想要使用类组件的人,我得到了答案。这实际上是无处可寻的!好好享受吧!
您将添加if(this.props.router.isReady)并将return包含在render()的条件中。

.
.
import { withRouter } from 'next/router';
import { Component } from 'react';

class User extends Component {

    ...
    
    render() { 
        if(this.props.router.isReady){ // Add this condition and include return ()

            // Do anything

            console.log(this.props.router.query) // Log 'query' on first render

            return (
                <div>
                    <SearchBar pid={this.props.router.query.pid} /> // Pass the query params to another component if needed
                </div>
            );
            
        }
    }
}

export default withRouter(User);

相关问题