在 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 的 context
和 router
的 transitionTo
方法。
这是一个简单的例子:
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。
如果您想了解更多关于使用react
的context
的信息,请查看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】:
我发现使用状态、三元运算符和<Redirect>
效果最好。我认为这也是首选方式,因为它最接近 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 的人来说,这会起作用 ;) 为什么不使用路由器会将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
。它在底层使用相同的 Push
和 Replace
方法,但更具声明性【参考方案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 中以编程方式导航路由时不是一个函数