React类组件用useParams获取URL id报Invalid hook call错误求解
问题描述
需要从URL路径中提取路由参数id,测试访问地址为http://localhost:8080/passenger-update/16,实现过程中触发如下报错:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
- React与渲染器(如React DOM)版本不匹配
- 违反了Hooks调用规则
- 同一应用内存在多份React副本
当前项目技术栈为React 17、react-router-dom 6.3.0。
问题根因
报错本质是违反Hooks调用规则:useParams是React Router提供的Hook API,仅支持在函数组件、自定义Hook的顶层作用域调用,不能在类组件、类组件生命周期方法、普通嵌套函数中使用。现有代码在类组件PassengerUpdate的componentDidMount生命周期方法内直接调用useParams,和React版本冲突、多副本React问题无关。
关联代码
类组件原有实现
class PassengerUpdate extends Component { componentDidMount() { let { id } = useParams(); console.log(id); } render() { return ( <div className="container"> </div> ); } } export default PassengerUpdate;
路由配置
<Routes> <Route exact path="/passenger-update/:id" element={<PassengerUpdate/>}/> </Routes>
依赖配置
{ "private": true, "scripts": { "dev": "npm run development", "development": "mix", "watch": "mix watch", "watch-poll": "mix watch -- --watch-options-poll=1000", "hot": "mix watch --hot", "prod": "npm run production", "production": "mix --production" }, "devDependencies": { "@babel/preset-react": "^7.13.13", "@popperjs/core": "^2.10.2", "axios": "^0.25.0", "bootstrap": "^5.1.3", "laravel-mix": "^6.0.6", "lodash": "^4.17.19", "postcss": "^8.1.14", "react": "^17.0.2", "react-dom": "^17.0.2", "resolve-url-loader": "^5.0.0", "sass": "^1.32.11", "sass-loader": "^11.0.1" }, "dependencies": { "react-bootstrap": "^2.4.0", "react-router-dom": "^6.3.0" } }
可行解决方案
方案一:改写为函数组件(官方推荐)
直接将类组件重构为函数组件,在顶层作用域调用useParams获取参数,用useEffect实现原componentDidMount的生命周期逻辑,路由配置无需修改:
import { useEffect } from 'react'; import { useParams } from 'react-router-dom'; const PassengerUpdate = () => { // 函数组件顶层调用Hook,符合规则 const { id } = useParams(); useEffect(() => { // 此处等价于原componentDidMount执行逻辑 console.log('获取到的路由id:', id); // 后续可写详情查询、表单初始化等业务逻辑 }, [id]); return ( <div className="container"> {/* 页面原有内容 */} </div> ); } export default PassengerUpdate;
方案二:封装高阶组件注入路由参数
如果不想重构现有类组件,可自行封装高阶组件,在高阶组件内调用useParams拿到参数后,通过props传递给类组件使用:
import { Component } from 'react'; import { useParams } from 'react-router-dom'; // 封装通用withRouter高阶组件 const withRouter = (WrappedComponent) => { return (props) => { const params = useParams(); // 将路由参数作为props传入被包装的类组件 return <WrappedComponent {...props} params={params} />; }; }; class PassengerUpdate extends Component { componentDidMount() { // 从props中读取路由参数 const { id } = this.props.params; console.log('获取到的路由id:', id); // 原有业务逻辑保持不变 } render() { return ( <div className="container"> {/* 页面原有内容 */} </div> ); } } // 导出时用高阶组件包装即可,路由配置无需修改 export default withRouter(PassengerUpdate);
内容的提问来源于stack exchange,提问作者JFeel
相关产品推荐
相关产品推荐

