如何继承被withRouter包装的React.Component类?
React Router V6 类组件继承与withRouter冲突的解决方案
问题背景
在使用Router V6的React项目中,尝试给一个被多个子类继承的父类Form添加编程式路由跳转功能,代码结构及报错如下:
原代码结构
父类Form(继承自React.Component):
class Form extends React.Component { // ... 原有业务逻辑 } export default withRouter(Form);
子类LoginForm:
class LoginForm extends Form { // ... 子类业务逻辑 } export default LoginForm;
自定义withRouter实现:
const withRouter = Wrapper => props => { const params = useParams(); const navigate = useNavigate(); return ( <Wrapper {...props} navigate={navigate} params={params} /> ) } export default withRouter;
报错信息
Login.js:8 Uncaught TypeError: Class extends value props => { _s(); const params = (0,react_router_dom__WEBPACK_IMPORTED_MODULE_2__.useParams)(); cons...<omitted>... } is not a constructor or null at ./src/pages/Forms/Auth/Login.js (Login.js:8:1)
错误原因
核心冲突:JavaScript类组件只能继承React.Component或其派生类,无法继承函数组件。使用withRouter(Form)包装后,导出的Form已变成函数组件(withRouter返回的是函数组件),而LoginForm试图继承该函数组件,导致JS引擎抛出构造函数类型错误。
可行解决方案
方案1:调整导出逻辑,保留父类可继承性
不直接用withRouter包装父类Form导出,而是让所有子类在导出时单独包装,父类保持原始类组件形态:
- 修改
Form.js导出:
class Form extends React.Component { // ... 原有业务逻辑 } // 直接导出原始类组件,避免用withRouter包装 export default Form;
- 子类导出时用
withRouter包装:
class LoginForm extends Form { // ... 子类业务逻辑 } // 子类导出时注入路由属性 export default withRouter(LoginForm);
此方案保证父类Form始终是可继承的类组件,子类继承后再通过withRouter获取路由属性,规避了继承函数组件的问题。
方案2:使用Context注入路由属性(多子类场景首选)
通过React Context将路由相关的navigate和params注入组件树,父类Form通过Context获取属性,子类继承后自动拥有访问权限,无需逐个包装:
- 创建路由Context:
// src/contexts/RouterContext.js import { createContext, useNavigate, useParams } from 'react-router-dom'; const RouterContext = createContext({}); // 提供路由属性的Provider组件 export const RouterProvider = ({ children }) => { const navigate = useNavigate(); const params = useParams(); return ( <RouterContext.Provider value={{ navigate, params }}> {children} </RouterContext.Provider> ); }; export default RouterContext;
- 修改父类
Form获取路由属性:
import RouterContext from '../contexts/RouterContext'; class Form extends React.Component { // 声明使用RouterContext static contextType = RouterContext; // 在方法中使用编程式跳转 handleSubmit = () => { this.context.navigate('/dashboard'); }; } export default Form;
- 路由配置中用
RouterProvider包裹子类组件:
// src/routes.js import { Routes, Route } from 'react-router-dom'; import { RouterProvider } from './contexts/RouterContext'; import LoginForm from './pages/Forms/Auth/Login'; function AppRoutes() { return ( <Routes> <Route path="/login" element={ <RouterProvider> <LoginForm /> </RouterProvider> } /> {/* 其他路由同理 */} </Routes> ); } export default AppRoutes;
此方案无需修改子类继承逻辑,所有继承自Form的子类都能通过this.context访问路由属性,适合多子类的复杂场景。
内容的提问来源于stack exchange,提问作者Barry Chapman
相关产品推荐
相关产品推荐

