Next.js:使用状态的Router.push

78

我正在使用Next.js重构一个进行服务器端渲染的应用程序。 我有一个处理搜索请求的按钮。

在旧的应用程序中,处理程序如下:

search = (event) => {
    event.preventDefault();
    history.push({
        pathname: '/results',
        state: {
            pattern: this.state.searchText,
        }
    });
}

在结果类中,我可以使用 this.props.location.state.pattern 获取状态日期。

现在我正在使用 Next.js:

import Router, { withRouter } from 'next/router'

performSearch = (event) => {
    event.preventDefault();
    Router.push({ pathname: '/results', state: { pattern: this.state.searchText } });
};
在结果类中,我使用了

static async getInitialProps({req}) {
    return req.params;
}

我不确定是否需要将这个添加到我的server.js文件中:

server.get('/results', (req, res) => {
    return app.render(req, res, '/results', req.params)
})

然而,getInitialProps函数会抛出错误,因为req未定义。长话短说:如何在不使用GET参数的情况下将状态或参数传递到另一页?

7个回答

115
next.js中,您可以像这样传递查询参数。
Router.push({
    pathname: '/about',
    query: { name: 'Someone' }
})

然后在您的下一页(这里是/about页面),通过router属性检索query,需要使用withRouter将其注入到Component中。

import { withRouter } from 'next/router'

class About extends React.Component {
  // your Component implementation
  // retrieve them like this
  // this.props.router.query.name
}

export default withRouter(About)

3
这段代码提示了一个错误:Type Error -- 无法读取未定义的属性'query'。要修复这个错误,需要检查上下文中是否有适当的路由器和属性。如果存在,则应该检查是否正确传递了名称参数。该代码段是访问React组件属性(props)中的路由器对象(router),并尝试从中读取名称参数(name)。 - Hossain Misho
1
@ggnoredo 我猜你在 getInitialProps 中使用了 req.user,但是当客户端进行路由时,就没有 req 对象了。只有在 Next.js 在服务器上渲染时才存在 req.user - Prithwee Das
4
这会污染UR(可能指URL)。有没有类似React Router的功能? - Umakant Patil
1
没有这样的功能。您可以在此处了解更多信息:https://github.com/zeit/next.js/issues/771 @UmakantPatil - Prithwee Das
1
@ZenVentzi 我找不到类似的功能,也不知道是否有包可以模拟。通常我使用本地存储来保存对象,并在需要时检索它们。只需确保定期清理,不要占用所有空间。我希望未来的版本能够在页面切换完成客户端时放置状态。 - KeitelDOG
显示剩余7条评论

37
如果您希望保持URL的干净,可以按照Prithwee Das的答案进行小修改,如下所示。
Router.push({
    pathname: '/about',
    query: { name: 'Someone' }
}, '/about');

现在你可以使用props在组件中访问属性

...

const YourComponent = (props) => {
    useEffect(() => {
        console.log(props.router.query.name);
    }, [props.router.query]);

    return (
        <React.Fragment>
            ...
        </React.Fragment>
    );
};

...

这个对我有用!但是如果我传递嵌套对象,它们的子元素都变为空白(“”),不知道为什么... - Nasrul Arif
3
尝试在路由推送中使用JSON.stringify(obj),然后在useEffect钩子中使用JSON.parse(obj)。 - Ahmet Firat Keler
太棒了,非常棒! - Gabriel Arghire

21

我不知道它是否支持SSR,但为了避免错误cannot read property 'query' of undefined,必须按以下方式进行。

使用useRouter钩子获取访问URL,并导入如下。

import { useRouter } from 'next/router'
假设你想要从组件A将数据{name:'Someone'}传递到组件B 在组件A中,
const router = useRouter();

router.push(
  { pathname: "/path_of_component_b", query: { name: "Someone" } },
  "path_of_component_b"
);
在中,
const router = useRouter();

useEffect(() => {
  alert(router.query.name); // Alerts 'Someone'
}, [router.query]);

9

如果你想要“干净”的URL,一种实现方法是在链接上添加onClick处理程序,并将所需信息存储在上下文/Redux存储中。如果您已经有一个存储器,这很容易实现。

<Link href='...'>
  <a onClick={()=>{dispatch(....)}}>Link<a/>
<Link>

3
不要将太多内容放入Redux中。如果只有一两个组件涉及该状态,最好避免使用Redux或上下文。 - Genia
是的,基本上这是一个hack。但由于使用Next.js Router发送深度嵌套数据很难/不可能,因此它具有一些有限的用途。 - Aleks

1
如果您正在使用Next.js 13中引入的App Router功能和约定,那么大部分之前对于这个问题的回答都不适用或者根本不起作用。例如,pathname字符串已被移除并替换为usePathname(),query对象已被移除并替换为useSearchParams()。
这个页面包含了您正在寻找的答案:https://nextjs.org/docs/app/api-reference/functions/use-router

1
我想在 @Ahmet Firat Keler 的答案中添加。如果您想使您的 URL 保持干净并传递数据,我们可以使用 "next/link" 来实现。 as 属性与 next/link 配合良好,您也可以使用 next/link 隐藏查询参数。
<Link href={`/blogs/${item.slug}?id=${item.id}`} as={`/blogs/${item.slug}`} passHref>
    <a href="" className="hover:text-cyanBlue">
        Read More
    </a>
</Link>

但请记住,如果您将target="_blank"设置为锚标签或打开链接到浏览器的下一个选项卡,则此方法无法正常工作


1
在Next.js 13应用程序目录中,你不能像之前的答案那样传递参数,使用Next.js导航系统没有办法做到这一点。然而,你可以使用纯JavaScript来实现,通过使用history.pushState(state, "", url)。你可以在这里阅读更多相关信息。
以下是如何使用它的示例。 第一页:
"use client";
import { useRouter, usePathname } from "next/navigation";

export default function First() {
  const router = useRouter();
  const pathname = usePathname();

  function handleClick() {
    history.pushState({ email: "example@example.com" }, "", pathname + "/second");
    router.push("second");
  }

  return <button onClick={handleClick}>Navigate to Second page</button>;
}


第二页:
"use client";
import { useEffect, useState } from "react";

const myState = history.state;//store the state variable outside the component

export default function Second() {
  const [routeState, setRouteState] = useState({});

  useEffect(() => {
    setRouteState(myState);
  }, []);

  return <h1>{routeState.email}</h1>;
}

注意:这种方法只适用于客户端组件,并且在刷新浏览器后状态将消失,因此这种方法的使用场景非常有限。

调用history.pushStaterouter.push不会添加两个历史记录条目吗?在第二页使用const myState = history.state在组件外部意味着这将只在组件加载时执行一次,而不是在渲染时执行,也不会在后续渲染中执行。 - undefined
是的,我提到了在刷新页面后状态会消失,这意味着状态只会在初始加载时可用。然而,我认为现在是时候考虑使用localStorage作为状态或使用查询参数了。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接