React TypeScript中BrowserRouter编程式路由跳转TS2339错误
window.history.push的类型错误 这个问题我之前也碰到过,核心原因是你把浏览器原生的History API和React Router封装的history对象搞混了!浏览器原生的window.history根本没有push方法(它对应的是pushState),而你需要的无刷新路由跳转,得用React Router提供的导航方式才行。结合你说的「Router是当前组件的子元素」这个场景,给你两种主流版本的解决方案:
React Router v6+(当前主流版本)
因为你的myPage组件是Router的父组件(Router通过props.children传入),所以直接在myPage里用useNavigate钩子是不行的——路由钩子只能在Router上下文内部的组件中使用。这里有两种处理思路:
思路1:把导航逻辑移到Router内部组件
这是最符合React Router设计的方式,把点击跳转的逻辑放到Router内部的页面组件里,这样就能正常使用useNavigate:
import React from 'react'; import { useNavigate } from 'react-router-dom'; // 定义Router内部的导航组件 const NavTrigger: React.FC = () => { const navigate = useNavigate(); const loadPage = (event: React.MouseEvent<HTMLDivElement>) => { const target = event.target as HTMLDivElement; if (target.id) { navigate(`/${target.id}`); // 用React Router的导航方法 } }; return <div id='home' onClick={loadPage}></div>; }; // 你的myPage组件 const myPage: React.FC = (props) => { return ( <> {/* 渲染Router内部的导航组件 */} <NavTrigger /> <section> {props.children} {/* 这里是你的BrowserRouter */} </section> </> ); };
思路2:自定义History实例(适合必须在Router外部导航的场景)
如果你确实需要在Router父组件里处理跳转,可以创建一个自定义的history实例,传递给BrowserRouter:
首先安装依赖:
npm install history
然后修改代码:
import React from 'react'; import { BrowserRouter } from 'react-router-dom'; import { createBrowserHistory } from 'history'; // 创建自定义history实例 const history = createBrowserHistory(); const myPage: React.FC = (props) => { const loadPage = (event: React.MouseEvent<HTMLDivElement>) => { const target = event.target as HTMLDivElement; if (target.id) { history.push(`/${target.id}`); // 用自定义实例的push方法 } }; return ( <> <div id='home' onClick={loadPage}></div> <section> {/* 把自定义history传给BrowserRouter */} <BrowserRouter history={history}> {props.children} </BrowserRouter> </section> </> ); };
React Router v5(旧版本兼容)
如果还在使用React Router v5,同样分两种情况:
思路1:用withRouter高阶组件(Router内部组件)
把myPage用withRouter包装,让它能获取到React Router的history对象:
import React from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; // 定义props类型 type MyPageProps = RouteComponentProps & { children: React.ReactNode; }; const myPage: React.FC<MyPageProps> = (props) => { const loadPage = (event: React.MouseEvent<HTMLDivElement>) => { const target = event.target as HTMLDivElement; if (target.id) { props.history.push(`/${target.id}`); } }; return ( <> <div id='home' onClick={loadPage}></div> <section> {props.children} </section> </> ); }; // 用withRouter包装组件 export default withRouter(myPage);
思路2:自定义History实例(Router外部)
和v6的思路一致,创建自定义history实例并传给BrowserRouter即可,代码和v6版本几乎相同。
额外提醒
别想着用window.history.pushState来跳转——虽然它能改URL,但React Router不会感知到这个变化,也就不会更新对应的路由组件,等于白折腾。必须用React Router提供的导航方法,才能实现真正的无刷新路由跳转。
内容的提问来源于stack exchange,提问作者Bill

