React Router 5类组件点击按钮触发路由跳转实现方案
错误原因
useHistory是React Router提供的Hook API,Hook仅允许在函数组件顶层作用域、或自定义Hook内部调用。你在类组件的实例方法中调用useHistory,违反了Hook的调用规则,因此抛出Invalid hook call错误。
适配react-router-dom@5.3.0的实现方案
类组件中不要使用Hook实现跳转,可通过以下两种官方支持的方式实现和Link完全一致的无刷新前端路由跳转:
方案1:使用withRouter高阶组件(推荐)
withRouter是React Router v5专门为类组件提供的高阶组件,会自动将history/location/match三个路由对象注入到组件的props中。
注意:被withRouter包裹的组件必须放置在<Router>组件的子树范围内才能获取到路由上下文,因此需要先调整代码结构:
- 将
<Router>组件移动到应用入口文件(通常为src/index.js),包裹根组件App:
// src/index.js import React from 'react'; import ReactDOM from 'react-dom'; import { BrowserRouter as Router } from 'react-router-dom'; import App from './App'; ReactDOM.render( <Router> <App /> </Router>, document.getElementById('root') );
- 修改App组件,移除内部的Router声明,引入
withRouter包裹组件后直接通过props访问history实现跳转:
import React from 'react'; import { Link, Route, Switch, withRouter } from 'react-router-dom'; // Import pages import Page1 from './pages/page-1'; import Page2 from './pages/page-2'; class App extends React.Component { navigateToPage1 = () => { this.props.history.push('/page-1'); } navigateToPage2 = () => { this.props.history.push('/page-2'); } render() { return( <div className="container p-3 mx-auto"> <div className="pb-2"> <button onClick={this.navigateToPage1}>Visit Page 1</button> <button onClick={this.navigateToPage2}>Visit Page 2</button> <Link to="/page-1">Page 1</Link> <Link to="/page-2">Page 2</Link> </div> <div> <Switch> <Route exact path="/"> <Page1 /> </Route> <Route path="/page-1"> <Page1 /> </Route> <Route path="/page-2"> <Page2 /> </Route> </Switch> </div> </div> ); } } export default withRouter(App);
方案2:抽离子组件接入路由(无需调整Router位置)
如果你不想把Router移到App外层,可以把需要触发跳转的导航区域抽成独立类组件,用withRouter包裹后放在Router内部即可:
import React from 'react'; import { Link, BrowserRouter as Router, Route, Switch, withRouter } from 'react-router-dom'; // Import pages import Page1 from './pages/page-1'; import Page2 from './pages/page-2'; // 抽离导航组件 class NavSection extends React.Component { navigateToPage1 = () => { this.props.history.push('/page-1'); } navigateToPage2 = () => { this.props.history.push('/page-2'); } render() { return <div className="pb-2"> <button onClick={this.navigateToPage1}>Visit Page 1</button> <button onClick={this.navigateToPage2}>Visit Page 2</button> <Link to="/page-1">Page 1</Link> <Link to="/page-2">Page 2</Link> </div> } } const WrappedNavSection = withRouter(NavSection); class App extends React.Component { render() { return( <div className="container p-3 mx-auto"> <Router> <WrappedNavSection /> <div> <Switch> <Route exact path="/"> <Page1 /> </Route> <Route path="/page-1"> <Page1 /> </Route> <Route path="/page-2"> <Page2 /> </Route> </Switch> </div> </Router> </div> ); } } export default App;
以上两种方案的跳转逻辑和
Link组件完全一致,均基于HTML5 History API实现前端路由切换,不会触发浏览器整页刷新。
内容的提问来源于stack exchange,提问作者Developer
相关产品推荐
相关产品推荐

