如何使用React Router进行编程式导航
技术背景
在React应用中,使用React Router来管理路由是常见的做法。有时候,我们需要在代码中进行编程式导航,而不是仅仅依赖于<Link>
组件。不同版本的React Router提供了不同的方式来实现编程式导航。
实现步骤
React Router v6及以上
从React Router v6开始,useHistory()
钩子已被弃用,推荐使用 useNavigate
钩子进行编程式导航。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import { useNavigate } from "react-router-dom";
function HomeButton() { const navigate = useNavigate();
function handleClick() { navigate("/home"); }
return ( <button type="button" onClick={handleClick}> Go home </button> ); }
|
还可以使用 replace
选项来替换当前历史记录条目:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import { useNavigate } from "react-router-dom";
function SignupForm() { let navigate = useNavigate();
async function handleSubmit(event) { event.preventDefault(); await submitForm(event.target); navigate("../success", { replace: true }); }
return <form onSubmit={handleSubmit}>{/* ... */}</form>; }
|
React Router v5.1.0及以上(使用钩子和React >16.8)
可以使用 useHistory
钩子在函数组件中进行编程式导航:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { useHistory } from "react-router-dom";
function HomeButton() { let history = useHistory(); function handleClick() { history.push("/home"); }
return ( <button type="button" onClick={handleClick}> Go home </button> ); }
|
React Router v4
在React Router v4中有三种方法可以在组件内进行编程式路由:
1. 使用 withRouter
高阶组件
withRouter
高阶组件会将 history
对象作为组件的一个属性注入,这样就可以访问 push
和 replace
方法:
1 2 3 4 5 6 7 8 9 10
| import { withRouter } from 'react-router-dom'
const Button = withRouter(({ history }) => ( <button type='button' onClick={() => { history.push('/new-location') }} > Click Me! </button> ))
|
2. 使用组合并渲染 <Route>
可以渲染一个无路径的 <Route>
,它将始终匹配当前位置。<Route>
组件传递的属性与 withRouter
相同,因此可以通过 history
属性访问 history
方法:
1 2 3 4 5 6 7 8 9 10 11 12
| import { Route } from 'react-router-dom'
const Button = () => ( <Route render={({ history}) => ( <button type='button' onClick={() => { history.push('/new-location') }} > Click Me! </button> )} /> )
|
3. 使用上下文(不推荐)
只有在熟悉React的上下文模型时才使用此方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const Button = (props, context) => ( <button type='button' onClick={() => { context.history.push('/new-location') }} > Click Me! </button> )
Button.contextTypes = { history: React.PropTypes.shape({ push: React.PropTypes.func.isRequired }) }
|
React Router v3及以上
在组件中使用 this.props.router.push
进行导航:
1 2 3 4 5 6 7
| import { withRouter } from 'react-router';
class Example extends React.Component { };
export default withRouter(Example);
|
React Router v2及以上
可以直接操作 browserHistory
进行导航:
1 2
| import { browserHistory } from 'react-router'; browserHistory.push('/some/path');
|
在组件内,可以使用 this.props.history.push
:
1
| this.props.history.push('/some/path');
|
React Router v1.x.x
使用 History
混合(Mixin)或通过 this.props.history
访问历史记录:
1 2 3 4 5 6
| var Example = React.createClass({ mixins: [ History ], navigateToHelpPage () { this.history.pushState(null, `/help`); } })
|
React Router 0.13.x
使用 Navigation
混合(Mixin)或通过上下文访问路由器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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>); } });
|
核心代码
以下是不同版本的核心导航代码示例:
React Router v6
1 2 3 4 5 6
| import { useNavigate } from "react-router-dom";
function Component() { let navigate = useNavigate(); navigate("/posts"); }
|
React Router v5.1.0
1 2 3 4 5 6
| import { useHistory } from "react-router-dom";
function HomeButton() { let history = useHistory(); history.push("/home"); }
|
React Router v4
1 2 3 4 5 6 7 8 9
| import { withRouter } from 'react-router-dom';
class Home extends Component { componentDidMount() { this.props.history.push('/redirect-to'); } }
export default withRouter(Home);
|
最佳实践
- 使用最新版本:尽量使用最新版本的React Router,因为它们通常提供了更简洁和现代的API。
- 代码复用:将导航逻辑封装在函数或组件中,以便在多个地方复用。
- 错误处理:在导航时考虑错误处理,例如网络错误或权限问题。
常见问题
this.props.history
未定义:确保组件是由 <Route>
渲染的,或者使用 withRouter
高阶组件包裹组件。useHistory
钩子不可用:检查React Router版本是否支持 useHistory
钩子,或者升级到支持的版本。- 上下文使用问题:使用上下文时,确保定义了
contextTypes
,否则上下文将不可访问。