使用 React Router 以编程方式导航

通过react-router我可以使用Link元素来创建由 react 路由器本地处理的链接。

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

我想从下拉列表中进行导航,而不是从链接进行导航。如何在代码中执行此操作?这是什么this.context

我看到了Navigation混合器,但是没有混合器可以做到吗?

答案

带有钩子的 React Router v5.1.0

如果您使用 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>
  );
}

反应路由器 v4

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

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

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

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

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

1. 使用withRouter高阶组件

withRouter高阶组件将注入history对象作为该组件的支持。这使您无需处理context即可访问pushreplace方法。

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相同的道具,因此您将能够通过history道具访问history方法。

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

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

3. 使用上下文 *

但是你可能不应该

最后一个选项是只有在您熟悉使用 React 的上下文模型时才应使用的选项。尽管上下文是一个选项,但应该强调的是上下文是不稳定的 API,React 在其文档中有 “ 为什么不使用上下文”一节。因此,使用后果自负!

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 是最简单的实现方式,因此对于大多数用例来说,它们是最好的选择。

React-Router 5.1.0+答案(使用钩子和 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+答案

在 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+答案

在 3.0 及更高版本中,将路由器用作组件的支持。

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

React-Router 2.4.0+答案

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

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+答案

该版本与 1.x 向后兼容,因此不需要升级指南。仅通过示例就足够了。

也就是说,如果您希望切换到新模式,则路由器内有一个 browserHistory 模块,您可以通过该模块访问

import { browserHistory } from 'react-router'

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

browserHistory.push('/some/path')

进一步阅读: 历史导航


React-Router 1.xx答案

我不会介绍升级细节。您可以在《 升级指南》中阅读有关内容

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

这是使用 Mixin 的示例:

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

请注意,此 “ History来自 “ 球拍 / 历史记录”项目。不是来自 React-Router 本身。

如果由于某种原因(也许由于 ES6 类)不想使用 Mixin,则可以从this.props.history访问从路由器获得的历史记录。仅路由器提供的组件可以访问它。因此,如果要在任何子组件中使用它,则需要通过props将其作为属性向下传递。

您可以在其1.0.x 文档中了解有关新版本的更多信息。

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

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

React-Router 0.13.x答案

我遇到了同样的问题,只能使用 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>);
  }
});

我无需调用.context就可以调用transitionTo()

或者您可以尝试精美的 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

注意:如果您使用的是 Redux,那么还有另一个名为React-Router-Redux 的项目,它使用与React-Redux相同的方法为您提供 ReactRouter 的 redux 绑定。

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>

React - 路由器 v2

对于最新版本( v2.0.0-rc5 ),推荐的导航方法是直接推送到历史单例。您可以在Components doc 之外导航中看到这一点。

相关摘录:

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

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

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

它还提供pushState但已记录的警告已弃用。

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

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

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

这是在ES6 上使用react-router v2.0.0 v2.0.0 的方法react-router已从 mixins 移开。

import React from 'react';

export default class MyComponent extends React.Component {
  navigateToPage = () => {
    this.context.router.push('/my-route')
  };

  render() {
    return (
      <button onClick={this.navigateToPage}>Go!</button>
    );
  }
}

MyComponent.contextTypes = {
  router: React.PropTypes.object.isRequired
}

React-Router 4.x 回答:

最后,我希望有一个历史对象,甚至可以携带外部组件。我想做的是拥有一个按需导入的 history.js 文件,并对其进行操作。

您只需要将BrowserRouter更改为 Router,然后指定历史记录BrowserRouter即可。除了拥有自己的历史对象可以随意操作之外,这对您没有任何改变。

您需要安装历史记录react-router使用的库。

用法示例,ES6 表示法:

history.js

import createBrowserHistory from 'history/createBrowserHistory'
export default createBrowserHistory()

BasicComponent.js

import React, { Component } from 'react';
import history from './history';

class BasicComponent extends Component {

    goToIndex(e){
        e.preventDefault();
        history.push('/');
    }

    render(){
        return <a href="#" onClick={this.goToIndex}>Previous</a>;
    }
}

编辑 2018 年 4 月 16 日:

如果必须从实际从Route组件渲染的组件中导航,则还可以从 props 访问历史记录,如下所示:

BasicComponent.js

import React, { Component } from 'react';

class BasicComponent extends Component {

    navigate(e){
        e.preventDefault();
        this.props.history.push('/url');
    }

    render(){
        return <a href="#" onClick={this.navigate}>Previous</a>;
    }
}

对于这一点,谁不控制服务器端,因此使用哈希路由器 v2:

将您的历史记录放入单独的文件中(例如,app_history.js ES6):

import { useRouterHistory } from 'react-router'
import { createHashHistory } from 'history'
const appHistory = useRouterHistory(createHashHistory)({ queryKey: false });

export default appHistory;

并在任何地方使用它!

您的 react-router(app.js ES6)入口点:

import React from 'react'
import { render } from 'react-dom'
import { Router, Route, Redirect } from 'react-router'
import appHistory from './app_history'
...
const render((
  <Router history={appHistory}>
  ...
  </Router>
), document.querySelector('[data-role="app"]'));

您在任何组件(ES6)中的导航:

import appHistory from '../app_history'
...
ajaxLogin('/login', (err, data) => {
  if (err) {
    console.error(err); // login failed
  } else {
    // logged in
    appHistory.replace('/dashboard'); // or .push() if you don't need .replace()
  }
})

反应路由器 V4

tl:dr;

if (navigate) {
  return <Redirect to="/" push={true} />
}

简单且声明性的答案是,您需要将<Redirect to={URL} push={boolean} />setState()结合使用

push:布尔值 -如果为 true,则重定向会将新条目推入历史记录,而不是替换当前条目。


import { Redirect } from 'react-router'

class FooBar extends React.Component {
  state = {
    navigate: false
  }

  render() {
    const { navigate } = this.state

    // here is the important part
    if (navigate) {
      return <Redirect to="/" push={true} />
    }
   // ^^^^^^^^^^^^^^^^^^^^^^^

    return (
      <div>
        <button onClick={() => this.setState({ navigate: true })}>
          Home
        </button>
      </div>
    )
  }
}

完整的例子在这里在这里阅读更多。

PS。该示例使用ES7 + 属性初始化程序来初始化状态。如果您有兴趣,也请在这里查看。

警告:此答案仅涵盖 1.0 之前的 ReactRouter 版本

之后,我将以 1.0.0-rc1 用例更新此答案!

您也可以不使用 mixins。

let Authentication = React.createClass({
  contextTypes: {
    router: React.PropTypes.func
  },
  handleClick(e) {
    e.preventDefault();
    this.context.router.transitionTo('/');
  },
  render(){
    return (<div onClick={this.handleClick}>Click me!</div>);
  }
});

带有上下文的陷阱是,除非您在类上定义contextTypes ,否则无法访问它。

至于什么是上下文,它是一个像道具一样的对象,它从父级传递到子级,但是隐式地传递下来,而不必每次都重新声明道具。参见https://www.tildedave.com/2014/11/15/introduction-to-contexts-in-react-js.html

在正常工作之前,我尝试了至少 10 种方法!

@Felipe Skinner 的withRouter答案对我来说有点不知所措,而且我不确定是否要创建新的 “ExportedWithRouter” 类名称。

大约是当前的 React-Router 3.0.0 和 ES6,这是最简单,最干净的方法:

使用 ES6 的 React-Router 3.xx:

import { withRouter } from 'react-router';

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

// Export the decorated class
export default withRouter(Example);

或者,如果不是您的默认班级,则按以下方式导出:

withRouter(Example);
export { Example };

请注意,在 3.xx 中, <Link>组件本身正在使用router.push ,因此您可以将传递<Link to=标记的任何内容传递给它,例如:

this.props.router.push({pathname: '/some/path', query: {key1: 'val1', key2: 'val2'})'

要以编程方式进行导航,您需要将新的历史记录送到 componentprops.history ,这样可以为您完成工作:

//using ES6
import React from 'react';

class App extends React.Component {

  constructor(props) {
    super(props)
    this.handleClick = this.handleClick.bind(this)
  }

  handleClick(e) {
    e.preventDefault()
    /* Look at here, you can add it here */
    this.props.history.push('/redirected');
  }

  render() {
    return (
      <div>
        <button onClick={this.handleClick}>
          Redirect!!!
        </button>
      </div>
    )
  }
}

export default App;