使用反应路由器 V4 以编程方式导航 [英] Programmatically navigate using react router V4
问题描述
我刚刚将 react-router
从 v3 替换为 v4.
但我不确定如何以编程方式在 Component
的成员函数中导航.即在 handleClick()
函数中,我想在处理一些数据后导航到 /path/some/where
.我曾经这样做过:
import { browserHistory } from 'react-router'browserHistory.push('/path/some/where')
但是我在v4中找不到这样的接口.
如何使用 v4 导航?
如果你的目标是浏览器环境,你需要使用 react-router-dom
包,而不是 react-router代码>.他们遵循与 React 相同的方法,以便将核心 (
react
) 和特定于平台的代码 (react-dom
, react-native
) 的细微差别,您不需要安装两个单独的包,因此环境包包含您需要的一切.您可以将其添加到您的项目中:
yarn 添加 react-router-dom
或
npm i react-router-dom
您需要做的第一件事是提供一个
作为应用程序中最顶层的父组件.
使用 HTML5 history
API 并为您管理它,因此您不必担心自己实例化它并将其传递给 <BrowserRouter>
组件作为道具(正如您在以前的版本中需要做的那样).
在 V4 中,为了以编程方式导航,您需要访问 history
对象,该对象可通过 React context
获得,只要您有
provider 组件作为应用程序中最顶层的父组件.该库通过上下文公开 router
对象,该对象本身包含 history
作为属性.history
界面提供了多种导航方法,例如 push
、replace
和 goBack
等.您可以在此处查看整个属性和方法列表.
Redux/Mobx 用户的重要提示
如果您在应用程序中使用 redux 或 mobx 作为状态管理库,您可能会遇到应该感知位置但在触发 URL 更新后未重新呈现的组件的问题>
发生这种情况是因为 react-router
使用上下文模型将 location
传递给组件.
connect 和observer 都创建组件,其shouldComponentUpdate 方法对当前的props 和下一个props 进行浅层比较.这些组件只会在至少一个 prop 发生变化时重新渲染.这意味着为了确保它们在位置更改时更新,需要为它们提供一个在位置更改时更改的 prop.
解决这个问题的两种方法是:
- 将您的连接组件包装在一个无路径的
中.当前location
对象是
传递给它呈现的组件的 props 之一 - 用
withRouter
高阶组件包裹你的 connected 组件,这实际上具有相同的效果并注入location
作为 prop
抛开这些不谈,有四种以编程方式导航的方法,按推荐排序:
1.- 使用
它促进了声明式风格.在 v4 之前,
组件<Route/>
组件位于组件层次结构的顶部,必须事先考虑路由结构.但是,现在您可以在树中任何地方拥有
组件,让您可以根据 URL 更好地控制条件渲染.Route
将 match
、location
和 history
作为道具注入到您的组件中.导航方法(例如 push
、replace
、goBack
...)可作为 history
的属性使用对象.
有 3 种方法可以使用 Route
渲染某些内容,分别使用 component
、render
或 children
props,但不要在同一个 Route
中使用多个.选择取决于用例,但基本上前两个选项只会在 path
匹配 url 位置时呈现您的组件,而对于 children
组件将被呈现路径是否与位置匹配(用于根据 URL 匹配调整 UI).
如果你想自定义你的组件渲染输出,你需要将你的组件包装在一个函数中并使用 render
选项,以便将任何除了 match
、location
和 history
之外,您想要的其他道具.举例说明:
import { BrowserRouter as Router } from 'react-router-dom'const ButtonToNavigate = ({ title, history }) =>(<按钮类型=按钮"onClick={() =>history.push('/my-new-location')}>{标题}按钮>);const SomeComponent = () =>(<Route path="/" render={(props) =><ButtonToNavigate {...props} title="在别处导航"/>}/>)const App = () =>(<路由器><某些组件/>//注意在 v4 中我们可以让任何其他组件交错<另一个组件/></路由器>);
2.- 使用
withRouter
HoC
这个高阶组件将注入与 Route
相同的 props.但是,它存在每个文件只能有 1 个 HoC 的限制.
import { withRouter } from 'react-router-dom'const ButtonToNavigate = ({ history }) =>(<按钮类型=按钮"onClick={() =>history.push('/my-new-location')}>导航按钮>);ButtonToNavigate.propTypes = {历史:React.PropTypes.shape({推送:React.PropTypes.func.isRequired,}),};导出默认 withRouter(ButtonToNavigate);
3.- 使用 Redirect
组件
渲染
将导航到新位置.但请记住,默认情况下,当前位置会替换为新位置,例如服务器端重定向 (HTTP 3xx).新位置由 to
属性提供,它可以是一个字符串(重定向到的 URL)或一个 location
对象.如果您想将新条目推送到历史记录,请同时传递一个 push
属性并将其设置为 true
4.- 通过上下文手动访问 router
有点气馁,因为 context 仍然是一个实验性的 API,它很可能在 React 的未来版本中被破坏/改变const ButtonToNavigate = (props, context) =>(<按钮类型=按钮"onClick={() =>context.router.history.push('/my-new-location')}>导航到新位置按钮>);ButtonToNavigate.contextTypes = {路由器:React.PropTypes.shape({历史:React.PropTypes.object.isRequired,}),};
不用说,还有其他路由器组件是为非浏览器生态系统设计的,例如
复制导航堆栈在内存中和目标 React Native 平台,可通过 react-router-native
包获得.
如需进一步参考,请随时查看官方文档.还有一个视频,由该库的一位合著者制作,提供了一个react-router v4 的介绍非常棒,重点介绍了一些主要变化.
I have just replaced react-router
from v3 to v4.
But I am not sure how to programmatically navigate in the member function of a Component
.
i.e in handleClick()
function I want to navigate to /path/some/where
after processing some data.
I used to do that by:
import { browserHistory } from 'react-router'
browserHistory.push('/path/some/where')
But I can't find such interfaces in v4.
How can I navigate using v4?
If you are targeting browser environments, you need to use react-router-dom
package, instead of react-router
. They are following the same approach as React did, in order to separate the core, (react
) and the platform specific code, (react-dom
, react-native
) with the subtle difference that you don't need to install two separate packages, so the environment packages contain everything you need. You can add it to your project as:
yarn add react-router-dom
or
npm i react-router-dom
The first thing you need to do is to provide a <BrowserRouter>
as the top most parent component in your application. <BrowserRouter>
uses the HTML5 history
API and manages it for you, so you don't have to worry about instantiating it yourself and passing it down to the <BrowserRouter>
component as a prop (as you needed to do in previous versions).
In V4, for navigating programatically you need to access the history
object, which is available through React context
, as long as you have a <BrowserRouter>
provider component as the top most parent in your application. The library exposes through context the router
object, that itself contains history
as a property. The history
interface offers several navigation methods, such as push
, replace
and goBack
, among others. You can check the whole list of properties and methods here.
Important Note to Redux/Mobx users
If you are using redux or mobx as your state management library in your application, you may have come across issues with components that should be location-aware but are not re-rendered after triggering an URL update
That's happening because react-router
passes location
to components using the context model.
Both connect and observer create components whose shouldComponentUpdate methods do a shallow comparison of their current props and their next props. Those components will only re-render when at least one prop has changed. This means that in order to ensure they update when the location changes, they will need to be given a prop that changes when the location changes.
The 2 approaches for solving this are:
- Wrap your connected component in a pathless
<Route />
. The currentlocation
object is one of the props that a<Route>
passes to the component it renders - Wrap your connected component with the
withRouter
higher-order component, that in fact has the same effect and injectslocation
as a prop
Setting that aside, there are four ways to navigate programatically, ordered by recommendation:
1.- Using a
It promotes a declarative style. Prior to v4, <Route>
Component<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.
There are 3 ways to render something with a Route
, by using either component
, render
or children
props, but don't use more than one in the same Route
. The choice depends on the use case, but basically the first two options will only render your component if the path
matches the url location, whereas with children
the component will be rendered whether the path matches the location or not (useful for adjusting the UI based on URL matching).
If you want to customise your component rendering output, you need to wrap your component in a function and use the render
option, in order to pass to your component any other props you desire, apart from match
, location
and history
. An example to illustrate:
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.- Using
withRouter
HoC
This higher order component will inject the same props as Route
. However, it carries along the limitation that you can have only 1 HoC per file.
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.- Using a Redirect
component
Rendering a <Redirect>
will navigate to a new location. But keep in mind that, by default, the current location is replaced by the new one, like server-side redirects (HTTP 3xx). The new location is provided by to
prop, that can be a string (URL to redirect to) or a location
object. If you want to push a new entry onto the history instead, pass a push
prop as well and set it to true
<Redirect to="/your-new-location" push />
4.- Accessing router
manually through context
A bit discouraged because context is still an experimental API and it is likely to break/change in future releases of Reactconst 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,
}),
};
Needless to say there are also other Router components that are meant to be for non browser ecosystems, such as <NativeRouter>
that replicates a navigation stack in memory and targets React Native platform, available through react-router-native
package.
For any further reference, don't hesitate to take a look at the official docs. There is also a video made by one of the co-authors of the library that provides a pretty cool introduction to react-router v4, highlighting some of the major changes.
这篇关于使用反应路由器 V4 以编程方式导航的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!