在 React-Router v4 中以编程方式导航

Posted

技术标签:

【中文标题】在 React-Router v4 中以编程方式导航【英文标题】:Navigating Programmatically in React-Router v4 【发布时间】:2017-02-15 02:43:18 【问题描述】:

我迫不及待地开始使用react-router v4 的最新 alpha 版本。全新的<BrowserRouter/> 非常适合让您的 UI 与浏览器历史记录保持同步,但是我如何使用它以编程方式进行导航?

【问题讨论】:

spik3s :请注意,接受的答案不起作用,但这个答案是:***.com/a/43250939/1533892 - 可能有助于读者接受那个答案。免责声明:工作答案是我的:) @HarlanTWood 谢谢!没有时间更新我的答案,感谢您的参与。已将您的答案标记为正确。 太好了,谢谢:) 【参考方案1】:

过去您可能使用browserHistory 推送新路径。这不适用于react-router v4。相反,您可以使用 React 的 contextroutertransitionTo 方法。

这是一个简单的例子:

import React from 'react';

class NavigateNext extends React.Component 
  constructor() 
    super();
    this.navigateProgramatically = this.navigateProgramatically.bind(this);
  

  navigateProgramatically(e) 
    e.preventDefault();

    this.context.router.transitionTo(e.target.href)
  

  render() 
    return (
      <Link to="/next-page"
            onClick=this.navigateProgramatically
      >Continue</Link>
    );
  


NavigateNext.contextTypes = 
  router: React.PropTypes.object
;

transitionTo 只是可用的router 方法之一。 router 对象还包含 blockTransitions(getPromptMessage)createHref(to)replaceWith(loc),值得一试。

这是提到上述方法的official react-router tutorial。 如果您想了解更多关于使用reactcontext 的信息,请查看docs。

【讨论】:

我正在尝试这个,但上下文对象中的路由器变量未定义。 “不知何故”它是 Link.js 中的有效变量。知道如何在 Link.js 之外获取路由器对象吗? 我正在使用 redux。如何从动作创建者访问路由器对象? 这对我不起作用。我最终发现你需要的一切都传递给了props 中的组件!您只需要this.props.history.push('/mypath')。我在下面的单独答案中给出了一个完整的例子:***.com/a/43250939/1533892 @Kaidjin 检查 this 以访问 redux actions 中的历史记录。【参考方案2】:

我没有足够的声誉来发表评论,但在回答@singularity 的问题时,您必须包含您希望在组件类'contextTypes 静态属性上提供的上下文属性。

来自the React docs on context

如果未定义 contextTypes,则 context 将是一个空对象。

在这种情况下:

class NavigateNext extends React.Component 

  // ...

  static contextTypes = 
    router: PropTypes.object
  

  // ...


propTypes 不同,contextTypes 实际上会导致 React 的行为不同,并且不仅用于类型检查。

【讨论】:

感谢您指出这一点。我在 propTypes 而不是 contextTypes 上设置了“路由器”。 感谢这是缺少的。现在我可以使用 this.context.history.goBack()【参考方案3】:

react-router v4 beta 发布,API 发生了一些变化。如果您使用的是最新版本,请不要使用this.context.router.transitionTo(e.target.href),而是使用this.context.router.push(e.target.href)

链接到新文档:https://reacttraining.com/react-router/#context.router

【讨论】:

我正在使用 react-router-dom 版本 4.1.2,我得到这个工作的唯一方法是使用 this.context.router.history.push('/')。也许这会对某人有所帮助。【参考方案4】:

我发现使用状态、三元运算符和&lt;Redirect&gt; 效果最好。我认为这也是首选方式,因为它最接近 v4 的设置方式。

在构造函数中()

this.state = 
    redirectTo: null
 
this.clickhandler = this.clickhandler.bind(this);

在render()中

render()
    return (
        <div>
         this.state.redirectTo ?
            <Redirect to= pathname: this.state.redirectTo  /> : 
            (
             <div>
               ..
             <button onClick= this.clickhandler  />
              ..
             </div>
             )
         

在clickhandler()中

 this.setState( redirectTo: '/path/some/where' );

希望对您有所帮助。告诉我。

【讨论】:

使用重定向不会导航到新的状态,而是会替换历史记录中的当前状态,因此无法返回。 @MikaTähtinen 这只是默认行为。您始终可以将 push 参数设置为 true,以便使用 push 而不是 replace【参考方案5】:

使用withRouter:

import React,  PropTypes  from 'react'
import  withRouter  from 'react-router'

// A simple component that shows the pathname of the current location
class ShowTheLocation extends React.Component 
  static propTypes = 
    match: PropTypes.object.isRequired,
    location: PropTypes.object.isRequired,
    history: PropTypes.object.isRequired
  

  render() 
    const  match, location, history  = this.props

    return (
      <div>You are now at location.pathname</div>
    )
  


// Create a new component that is "connected" (to borrow redux
// terminology) to the router.
const ShowTheLocationWithRouter = withRouter(ShowTheLocation)

【讨论】:

这与哪个 react-router 版本有关? v3 和 v4,我已包含指向 v4 文档的链接。【参考方案6】:

使用withRouter 将为您的组件添加路由器属性,然后您可以访问history 并使用push,就像您在v3 中所做的那样:

import React from 'react';
import  withRouter  from 'react-router-dom';

class Form extends React.Component 
  constructor(props) 
    super(props);

    this.state = 
      input: '',
    ;

    this._submit = this._submit.bind(this);
  

  render() 
    return (
      <form onSubmit=this._submit>
        <input type="text" onChange=(event) => this.setState(input: event.target.value)/>

        <button type="submit">Submit</button>
      </form>
    );
  

  _submit(event) 
    event.preventDefault();

    this.props.history.push(`/theUrlYouWantToGoTo`);
  


export default withRouter(Form);

【讨论】:

工作就像一个魅力,对于和我一样讨厌 React Router 的人来说,这会起作用 ;) 为什么不使用 或将 放在子组件中?【参考方案7】:

路由器会将history 对象添加到props 散列中的组件。因此,在您的组件中,只需执行以下操作:

this.props.history.push('/mypath')

这是一个完整的例子:

App.js:

import React from 'react'
import BrowserRouter as Router, Route from 'react-router-dom'

import Login from './Login'

export default class App extends React.Component 
  render() 
    return (
      <Router>
        <div>
          <Route exact path='/login' component=Login />
        </div>
      </Router>
    )
  

Login.js:

import React, PropTypes from 'react'

export default class Login extends React.Component 
  constructor(props) 
    super(props)
    this.handleLogin = this.handleLogin.bind(this)
  

  handleLogin(event) 
    event.preventDefault()
    // do some login logic here, and if successful:
    this.props.history.push(`/mypath`)
  

  render() 
    return (
      <div>
        <form onSubmit=this.handleLogin>
          <input type='submit' value='Login' />
        </form>
      </div>
    )
  

【讨论】:

哇谢谢。这节省了我的一天。与最新版本的 react 和 react-router 完美配合 抛出错误Cannot read property 'object' of undefined,有什么破解方法吗? @PardeepJain 你能包含堆栈跟踪的前几行吗? 这更像是一种命令式的方法。您应该改用Redirect。它在底层使用相同的 PushReplace 方法,但更具声明性【参考方案8】:

如果您需要在组件外部导航到您无法从组件传递历史对象的位置,类似于在旧版本中使用 browserHistory 的方式,您可以执行以下操作。

首先创建一个历史模块

History.js:

import createBrowserHistory from 'history/createBrowserHistory'

export default createBrowserHistory();

然后,当您声明路由器时,请确保从 react-router 而不是 react-router-dom 导入 Router(它只是 react-router 版本的包装器,但会自动创建历史对象)并传入历史模块刚刚创建

Root.js(或任何你这样做的地方):

import Router from 'react-router/Router'
import history from './history'

...

class Root extends Component
  render() 
    return (
      <Router history=history>
        ...
      </Router>
    );
  

现在您的应用程序将使用您创建的自定义创建历史记录。您现在可以在任何地方导入该历史模块,然后执行 history.replace 等操作,就像您过去使用 browserHistory 所做的那样。

SomeModule.js:

import history from './history';

export default ()=>
  // redirecting to login page using history without having to pass it in 
  // from a component
  history.replace('/login')

当然这不是推荐的方式,就像在旧版本中使用 browserHistory 不是推荐的方式一样,因为服务器端渲染之类的东西不起作用,但如果你不关心,这通常是正确的解决方案。

这样做的一个额外好处是,您可以将历史对象扩充为像这样的已解析查询字符串参数:

import createBrowserHistory from 'history/createBrowserHistory'
import queryString from 'query-string';

const history = createBrowserHistory();

history.location.query = queryString.parse(history.location.search);

history.listen(() => 
  history.location.query = queryString.parse(history.location.search);
);

export default history;

【讨论】:

【参考方案9】:

如果你需要访问组件之外的history(例如在redux actions中)react-router 已经发布了他们的原始解决方案here。

基本上你必须创建自己的历史对象:

import  createBrowserHistory  from 'history';

const history = createBrowserHistory();

并将其传递给您的路由器:

import  Router  from 'react-router-dom';

ReactDOM.render((
  <Router history=history> // <<-- the history object
    <App/>
  </Router>
), document.getElementById('root'))

注意:你必须在这里使用普通路由器而不是 BrowserRouter 或 HashRouter!

如果您现在导出history,您可以在任何地方使用它:

import history from './history';

history.push('/home');

【讨论】:

我可以使用 withRouter HOC 访问历史记录吗?就像文档说的那样,我们将不得不在任何地方使用这个历史对象......并且在继续使用这个之后,位置对象是否可以与路由器 HOC 一起使用? @Rajiv 是的,你仍然可以使用 withRouter HOC 我已经开始使用它了.. 谢谢.. 工作就像一个魅力.. :) 这就是答案【参考方案10】:

react-router 真的很难。没有一个选项是直截了当的。 this.props.history 给了我undefined。但是

window.location='/mypath/';

version 5.0.0 为我工作。不知道是不是正确的方法。

【讨论】:

这是 javascript 中的重定向,并不是真正适合 SPA 的方法

以上是关于在 React-Router v4 中以编程方式导航的主要内容,如果未能解决你的问题,请参考以下文章

在 react-router V4 中,如何以编程方式设置查询字符串参数?

如何在 react-router 中以编程方式进行服务器端路由?

react-router v4:以编程方式触发重定向(无需渲染 <Redirect />)

Uncaught TypeError: (0 , _reactRouter.withRouter) 在 react-router 2.4.0 中以编程方式导航路由时不是一个函数

如何在 QnA Maker v4.0 中以编程方式获取一对的 id?

React-router v4 this.props.history.push(...) 不工作