react-router-dom@6中mapStateToProps读取params报错及适配问题
问题背景
升级到react-router-dom@6后出现以下错误:
Cannot read properties of undefined (reading 'params') TypeError: Cannot read properties of undefined (reading 'params') at Function.mapStateToProps [as mapToProps]
尝试改用useParams时触发Hook规则错误:
React Hook
useParamsis called in functionmapStateToPropsthat is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use" react-hooks/rules-of-hooks
现有核心代码如下:
组件内部逻辑
const [product, setProduct] = useState({ ...props.product }); useEffect(() => { if (categories.length === 0) { getCategories() } setProduct({ ...props.product }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [props.product]);
Redux mapStateToProps
export function getProductById(products, productId) { let product = products.find(product => product.id === productId) || null; return product; } function mapStateToProps(state, ownProps) { const productId = ownProps.match.params.productId; const product = productId && state.productListReducer.length > 0 ? getProductById(state.productListReducer, productId) : {} return { product: product, products: state.productListReducer, categories: state.categoryListReducer } }
路由配置
<Routes> <Route path="/" element={<DashBoard></DashBoard>}></Route> <Route path="/cart" element={<CartDetails></CartDetails>}></Route> <Route path="/saveproduct/:productId" element={<AddOrUpdateProduct></AddOrUpdateProduct>}> </Route> </Routes>
解决方案
React Router v6不再向组件传递match、history等props,且Hooks只能在组件/自定义Hook中调用,因此需要重构代码:
1. 组件内直接获取路由参数与Redux状态
移除mapStateToProps,改用useParams(获取路由参数)和useSelector(获取Redux状态),代码如下:
import { useParams, useNavigate } from 'react-router-dom'; import { useSelector, useDispatch } from 'react-redux'; import { useState, useEffect } from 'react'; import { getCategories } from './your-action-path'; // 替换为实际action文件路径 import { getProductById } from './your-utils-path'; // 替换为实际工具函数路径 function AddOrUpdateProduct() { // 获取路由参数 const { productId } = useParams(); // 获取Redux状态 const products = useSelector(state => state.productListReducer); const categories = useSelector(state => state.categoryListReducer); // 获取dispatch方法 const dispatch = useDispatch(); // 替代history的导航方法 const navigate = useNavigate(); // 计算当前需要的product const currentProduct = productId && products.length > 0 ? getProductById(products, productId) : {}; const [localProduct, setLocalProduct] = useState({ ...currentProduct }); useEffect(() => { if (categories.length === 0) { dispatch(getCategories()); } setLocalProduct({ ...currentProduct }); }, [currentProduct, categories, dispatch]); // 示例:替代原history.push逻辑 const handleNavigate = () => { navigate('/cart'); }; // 组件其他业务逻辑... return ( // 组件JSX结构... ); } export default AddOrUpdateProduct;
2. 移除connect包裹
如果之前用connect(mapStateToProps)(AddOrUpdateProduct)导出组件,现在直接导出组件即可,无需connect包裹。
关键说明
- React Router v6取消了props传递
match/history的机制,必须通过useParams/useNavigate等Hooks获取路由相关能力 useParams是React Hook,只能在React组件(首字母大写)或自定义Hook(以use开头)中调用,不能在mapStateToProps这类普通函数中使用useSelector可以直接替代mapStateToProps的作用,灵活获取Redux状态中的数据
内容的提问来源于stack exchange,提问作者Emir Burak

