开发者问题收集

如何使用 React Router 以编程方式导航?

2015-06-26
1712704

使用 react-router ,我可以使用 Link 元素创建由 react router 原生处理的链接。

我看到它在内部调用 this.context.transitionTo(...)

我想进行导航。不是从链接,而是从下拉选择(作为示例)。我如何在代码中执行此操作?什么是 this.context

我看到了 Navigation mixin,但我可以在没有 mixins 的情况下执行此操作吗?

3个回答

UPDATE: 2022: React Router v6.6.1 with useNavigate

useHistory() 钩子现已弃用。如果您使用的是 React Router 6,则以编程方式导航的正确方法如下:

import { useNavigate } from "react-router-dom";

function HomeButton() {
  const navigate = useNavigate();

  function handleClick() {
    navigate("/home");
  }

  return (
    <button type="button" onClick={handleClick}>
      Go home
    </button>
  );
}

React Router v5.1.0 with hooks

如果您使用的是 React >16.8.0 和功能组件,则 React Router >5.1.0 中有一个新的 useHistory 钩子。

import { useHistory } from "react-router-dom";

function HomeButton() {
  const history = useHistory();

  function handleClick() {
    history.push("/home");
  }

  return (
    <button type="button" onClick={handleClick}>
      Go home
    </button>
  );
}

React Router v4

使用 React Router v4,您可以通过三种方法在组件内进行编程路由。

  1. 使用 withRouter 高阶组件。
  2. 使用组合并渲染 <Route>
  3. 使用 context

React Router 主要是 history 库。 history 使用其浏览器和哈希历史记录为您处理与浏览器的 window.history 的交互。它还提供内存历史记录,这对于没有全局历史记录的环境很有用。这在移动应用程序开发( react-native )和使用 Node 进行单元测试时特别有用。

history 实例有两种导航方法: pushreplace 。如果您将 history 视为访问过位置的数组,则 push 将向数组中添加新位置,而 replace 将用新位置替换数组中的当前位置。通常,您会希望在导航时使用 push 方法。

在 React Router 的早期版本中,您必须创建自己的 history 实例,但在 v4 中, <BrowserRouter><HashRouter><MemoryRouter> 组件将为您创建浏览器、哈希和内存实例。React Router 通过 router 对象下的上下文使与您的路由器关联的 history 实例的属性和方法可用。

1.使用 withRouter 高阶组件

withRouter 高阶组件将注入 history 对象作为组件的 prop。这样您就可以访问 pushreplace 方法,而无需处理 context

import { withRouter } from 'react-router-dom'
// this also works with react-router-native

const Button = withRouter(({ history }) => (
  <button
    type='button'
    onClick={() => { history.push('/new-location') }}
  >
    Click Me!
  </button>
))

2. 使用组合并呈现 <Route>

<Route> 组件不仅仅用于匹配位置。您可以呈现无路径路线,并且 它将始终与当前位置匹配 <Route> 组件传递与 withRouter 相同的 props,因此您将能够通过 history prop 访问 history 方法。

import { Route } from 'react-router-dom'

const Button = () => (
  <Route render={({ history}) => (
    <button
      type='button'
      onClick={() => { history.push('/new-location') }}
    >
      Click Me!
    </button>
  )} />
)

3. 使用 context*

但你可能不应该

最后一个选项是仅当你觉得使用 React 的 context 模型 (React 的 Context API 从 v16 开始稳定) 时才应使用的选项。

const Button = (props, context) => (
  <button
    type='button'
    onClick={() => {
      // context.history.push === history.push
      context.history.push('/new-location')
    }}
  >
    Click Me!
  </button>
)

// you need to specify the context type so that it
// is available within the component
Button.contextTypes = {
  history: React.PropTypes.shape({
    push: React.PropTypes.func.isRequired
  })
}

1 和 2 是最简单的实现选择,因此对于大多数用例来说,它们是你最好的选择。

Paul S
2017-02-08

React-Router v6+ Answer

TL;DR: 您可以使用新的 useNavigate 钩子。

import { useNavigate } from "react-router-dom";

function Component() {
  let navigate = useNavigate();
  // Somewhere in your code, e.g. inside a handler:
  navigate("/posts"); 
}

useNavigate 钩子返回一个可用于编程导航的函数。

来自 React Router 文档

import { useNavigate } from "react-router-dom";

function SignupForm() {
  let navigate = useNavigate();

  async function handleSubmit(event) {
    event.preventDefault();
    await submitForm(event.target);
    navigate("../success", { replace: true });
    // replace: true will replace the current entry in 
    // the history stack instead of adding a new one.

  }

  return <form onSubmit={handleSubmit}>{/* ... */}</form>;
}

React-Router 5.1.0+ Answer (using hooks and React >16.8)

您可以在功能组件上使用 useHistory 钩子并以编程方式导航:

import { useHistory } from "react-router-dom";

function HomeButton() {
  let history = useHistory();
  // use history.push('/some/path') here
};

React-Router 4.0.0+ Answer

在 4.0 及更高版本中,使用历史记录作为您的组件。

class Example extends React.Component {
   // use `this.props.history.push('/some/path')` here
};

注意:如果您的组件未由 <Route> 呈现,则 this.props.history 不存在。您应该使用 <Route path="..." component={YourComponent}/> 来在 YourComponent 中拥有 this.props.history

React-Router 3.0.0+ Answer

在 3.0 及更高版本中,使用路由器作为组件的 prop。

class Example extends React.Component {
   // use `this.props.router.push('/some/path')` here
};

React-Router 2.4.0+ Answer

在 2.4 及更高版本中,使用高阶组件将路由器作为组件的 prop。

import { withRouter } from 'react-router';

class Example extends React.Component {
   // use `this.props.router.push('/some/path')` here
};

// Export the decorated class
var DecoratedExample = withRouter(Example);

// PropTypes
Example.propTypes = {
  router: React.PropTypes.shape({
    push: React.PropTypes.func.isRequired
  }).isRequired
};

React-Router 2.0.0+ Answer

此版本向后兼容 1.x,因此不需要升级指南。只需浏览示例就足够了。

也就是说,如果您希望切换到新模式,路由器内部有一个 browserHistory 模块,您可以使用它来访问它

import { browserHistory } from 'react-router'

现在您可以访问浏览器历史记录,因此您可以执行推送、替换等操作...例如:

browserHistory.push('/some/path')

进一步阅读: Histories Navigation


React-Router 1.x.x Answer

我不会详细介绍升级细节。您可以在 升级指南

此处问题的主要变化是从 Navigation mixin 更改为 History 。现在它使用浏览器 historyAPI 来更改路由,因此从现在开始我们将使用 pushState()

这是一个使用 Mixin 的示例:

var Example = React.createClass({
  mixins: [ History ],
  navigateToHelpPage () {
    this.history.pushState(null, `/help`);
  }
})

请注意,此 History 来自 rackt/history 项目。不是来自 React-Router 本身。

如果您出于某种原因不想使用 Mixin(可能是因为 ES6 类),那么您可以从 this.props.history 访问从路由器获取的历史记录。它只能由您的 Router 呈现的组件访问。因此,如果您想在任何子组件中使用它,则需要通过 props 将其作为属性传递下去。

您可以在他们的 1.0.x 文档 中阅读有关新版本的更多信息。

这里是 一个专门关于在组件外部导航的帮助页面

它建议获取引用 history = createHistory() 并在其上调用 replaceState

React-Router 0.13.x Answer

我遇到了同样的问题,只能使用 react-router 附带的 Navigation mixin 找到解决方案。

这是我的做法它

import React from 'react';
import {Navigation} from 'react-router';

let Authentication = React.createClass({
  mixins: [Navigation],

  handleClick(e) {
    e.preventDefault();

    this.transitionTo('/');
  },

  render(){
    return (<div onClick={this.handleClick}>Click me!</div>);
  }
});

我能够调用 transitionTo() ,而无需访问 .context

或者您可以尝试花哨的 ES6 class

import React from 'react';

export default class Authentication extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick(e) {
    e.preventDefault();

    this.context.router.transitionTo('/');
  }

  render(){
    return (<div onClick={this.handleClick}>Click me!</div>);
  }
}

Authentication.contextTypes = {
  router: React.PropTypes.func.isRequired
};

React-Router-Redux

Note: if you're using Redux, there is another project called React-Router-Redux that gives you redux bindings for ReactRouter, using somewhat the same approach that React-Redux does

React-Router-Redux 有几种可用的方法,允许从动作创建者内部进行简单的导航。这些方法对于那些在 React Native 中已有架构的人来说尤其有用,他们希望在 React Web 中使用相同的模式,同时尽量减少样板开销。

探索以下方法:

  • push(location)
  • replace(location)
  • go(number)
  • goBack()
  • goForward()

以下是使用 Redux-Thunk 的示例:

./actioncreators.js

import { goBack } from 'react-router-redux'

export const onBackPress = () => (dispatch) => dispatch(goBack())

./viewcomponent.js

<button
  disabled={submitting}
  className="cancel_button"
  onClick={(e) => {
    e.preventDefault()
    this.props.onBackPress()
  }}
>
  CANCEL
</button>

Felipe Skinner
2015-06-26

React-Router v2

对于最新版本 ( v2.0.0-rc5 ),建议的导航方法是直接推送到历史单例。您可以在 在组件之外导航文档 中看到实际操作。

相关摘录:

import { browserHistory } from 'react-router';
browserHistory.push('/some/path');

如果使用较新的 react-router API,则需要在组件内部使用 this.props 中的 history ,因此:

this.props.history.push('/some/path');

它还提供 pushState ,但根据记录的警告,该功能已被弃用。

如果使用 react-router-redux ,它会提供一个 push 函数,您可以像这样分派:

import { push } from 'react-router-redux';
this.props.dispatch(push('/some/path'));

但是,这可能仅用于更改 URL,而不能实际导航到页面。

Bobby
2016-01-18