我刚刚把react-router从v3替换为v4。 但我不确定如何以编程方式在组件的成员函数中导航。 即在handleClick()函数中,我想在处理一些数据后导航到/path/some/where。 我以前是这样做的:
import { browserHistory } from 'react-router'
browserHistory.push('/path/some/where')
但是我在v4中找不到这样的界面。 如何使用v4导航?
我刚刚把react-router从v3替换为v4。 但我不确定如何以编程方式在组件的成员函数中导航。 即在handleClick()函数中,我想在处理一些数据后导航到/path/some/where。 我以前是这样做的:
import { browserHistory } from 'react-router'
browserHistory.push('/path/some/where')
但是我在v4中找不到这样的界面。 如何使用v4导航?
当前回答
第一步:只需要在上面导入一个东西:
import {Route} from 'react-router-dom';
第二步:在Route中,传递历史记录:
<Route
exact
path='/posts/add'
render={({history}) => (
<PostAdd history={history} />
)}
/>
第三步:历史被接受为下一个组件的道具的一部分,所以你可以简单地:
this.props.history.push('/');
这很简单,也很有力。
其他回答
如果你的目标是浏览器环境,你需要使用react-router-dom包,而不是react-router。他们遵循与React相同的方法,为了分离核心(React)和特定于平台的代码(React -dom, React -native),有细微的区别,你不需要安装两个单独的包,所以环境包包含了你需要的一切。你可以把它添加到你的项目中:
Yarn添加react-router-dom
or
我react-router-dom
您需要做的第一件事是提供一个<BrowserRouter>作为应用程序中最顶层的父组件。<BrowserRouter>使用HTML5历史API并为您管理它,因此您不必担心自己实例化它并将其传递给<BrowserRouter>组件作为道具(正如您在以前的版本中需要做的那样)。
在V4中,为了以编程方式导航,你需要访问历史对象,它可以通过React上下文获得,只要你在应用程序中有一个<BrowserRouter>提供者组件作为最顶层的父组件。该库通过上下文公开路由器对象,该对象本身包含历史作为属性。历史界面提供了一些导航方法,例如push、replace和goBack等。您可以在这里查看整个属性和方法列表。
Redux/Mobx用户注意事项 如果您在应用程序中使用redux或mobx作为状态管理库,那么您可能会遇到组件应该是位置感知的,但在触发URL更新后没有重新呈现的问题
这是因为react-router使用上下文模型将位置传递给组件。
connect和observer都创建了组件,它们的shouldComponentUpdate方法会对当前的道具和下一个道具进行简单的比较。这些组件只有在至少一个道具发生变化时才会重新渲染。这意味着,为了确保它们在位置更改时更新,它们将需要在位置更改时更改道具。
解决这个问题的两种方法是:
将连接的组件包装在一个无路径的<Route />。当前位置对象是<Route>传递给它呈现的组件的道具之一 使用withRouter高阶组件包装连接的组件,实际上具有相同的效果,并将位置作为道具注入
撇开这个不谈,有四种按推荐顺序进行编程导航的方法:
1.- Using a <Route> Component It promotes a declarative style. Prior to v4, <Route /> components were placed at the top of your component hierarchy, having to think of your routes structure beforehand. However, now you can have <Route> components anywhere in your tree, allowing you to have a finer control for conditionally rendering depending on the URL. Route injects match, location and history as props into your component. The navigation methods (such as push, replace, goBack...) are available as properties of the history object.
有三种方法来渲染一个Route,通过使用组件,渲染或子道具,但不要在同一个Route中使用多个。选择取决于用例,但基本上前两个选项只在路径与url位置匹配时才会呈现组件,而对于子组件,无论路径与位置是否匹配都将呈现(这对于基于url匹配调整UI很有用)。
如果你想定制你的组件渲染输出,你需要将你的组件包装在一个函数中,并使用渲染选项,以便传递给你的组件任何其他你想要的道具,除了匹配,位置和历史记录。举例说明:
import { BrowserRouter as Router } from 'react-router-dom'
const ButtonToNavigate = ({ title, history }) => (
<button
type="button"
onClick={() => history.push('/my-new-location')}
>
{title}
</button>
);
const SomeComponent = () => (
<Route path="/" render={(props) => <ButtonToNavigate {...props} title="Navigate elsewhere" />} />
)
const App = () => (
<Router>
<SomeComponent /> // Notice how in v4 we can have any other component interleaved
<AnotherComponent />
</Router>
);
2.-与throuter HoC一起使用
这个高阶组件将注入与Route相同的道具。但是,它有一个限制,每个文件只能有1个HoC。
import { withRouter } from 'react-router-dom'
const ButtonToNavigate = ({ history }) => (
<button
type="button"
onClick={() => history.push('/my-new-location')}
>
Navigate
</button>
);
ButtonToNavigate.propTypes = {
history: React.PropTypes.shape({
push: React.PropTypes.func.isRequired,
}),
};
export default withRouter(ButtonToNavigate);
3.使用重定向组件渲染<Redirect>将导航到一个新的位置。但是请记住,默认情况下,当前位置将被新位置替换,如服务器端重定向(HTTP 3xx)。新的位置由to prop提供,它可以是一个字符串(要重定向到的URL)或一个location对象。如果您想将一个新条目推送到历史记录中,也可以传递一个推送道具,并将其设置为true
<Redirect to="/your-new-location" push />
4.有点不鼓励,因为上下文仍然是一个实验性的API,它很可能在React的未来版本中被破坏/改变
const ButtonToNavigate = (props, context) => (
<button
type="button"
onClick={() => context.router.history.push('/my-new-location')}
>
Navigate to a new location
</button>
);
ButtonToNavigate.contextTypes = {
router: React.PropTypes.shape({
history: React.PropTypes.object.isRequired,
}),
};
不用说,还有其他的路由器组件是针对非浏览器生态系统的,比如<NativeRouter>,它在内存中复制了一个导航堆栈,目标是React Native平台,可以通过React - Router - Native包获得。
如需进一步参考,请参阅官方文档。还有一个由该库的共同作者之一制作的视频,该视频非常酷地介绍了react-router v4,突出了一些主要的变化。
你也可以简单地使用props来访问历史对象:
我的答案和Alex的相似。我不知道为什么React-Router把这个做得如此复杂。为什么我必须用一个HoC来包装我的组件,只是为了访问本质上是全局的?
不管怎样,如果你看一下他们是如何实现<BrowserRouter>的,它只是一个历史记录的小包装。
我们可以把这段历史提取出来,这样我们就可以从任何地方导入。然而,诀窍在于,如果您正在进行服务器端呈现,并试图导入历史模块,那么它将无法工作,因为它使用的是纯浏览器api。但这没关系,因为我们通常只在响应单击或其他客户端事件时重定向。因此,假装一下是可以的:
// history.js
if(__SERVER__) {
module.exports = {};
} else {
module.exports = require('history').createBrowserHistory();
}
在webpack的帮助下,我们可以定义一些变量,这样我们就知道我们所处的环境:
plugins: [
new DefinePlugin({
'__SERVER__': 'false',
'__BROWSER__': 'true', // you really only need one of these, but I like to have both
}),
现在你可以
import history from './history';
从任何地方。它只会在服务器上返回一个空模块。
如果你不想使用这些神奇的变量,你只需要在需要它的全局对象中(在你的事件处理程序中)。导入不能工作,因为它只在顶层工作。
因为有时我更喜欢通过应用程序切换路由,然后通过按钮,这是一个最小的工作示例,对我有用:
import { Component } from 'react'
import { BrowserRouter as Router, Link } from 'react-router-dom'
class App extends Component {
constructor(props) {
super(props)
/** @type BrowserRouter */
this.router = undefined
}
async handleSignFormSubmit() {
await magic()
this.router.history.push('/')
}
render() {
return (
<Router ref={ el => this.router = el }>
<Link to="/signin">Sign in</Link>
<Route path="/signin" exact={true} render={() => (
<SignPage onFormSubmit={ this.handleSignFormSubmit } />
)} />
</Router>
)
}
}
如此:
import { withRouter } from 'react-router-dom';
const SomeComponent = withRouter(({ history }) => (
<div onClick={() => history.push('/path/some/where')}>
some clickable element
</div>);
);
export default SomeComponent;