无法在mapStateToProps中访问useParams获取的productId路由参数
问题:mapStateToProps中无法获取useParams的productId
我需要获取URL中的productId变量,查资料得知可通过useParams钩子实现。组件内部能正常访问该变量,但在mapStateToProps函数中使用时,变量值为undefined。
相关代码
AddOrUpdateProduct.js
import React, { useState, useEffect } from "react"; import { connect } from "react-redux"; import { saveProduct, getProducts } from "../../redux/actions/productActions"; import { getCategories } from "../../redux/actions/categoryActions"; import ProductDetail from "./ProductDetail"; import { useSelector } from "react-redux"; import { useParams } from "react-router-dom"; function AddOrUpdateProduct({ products, categories, getProducts, getCategories, saveProduct, history, ...props }) { let {productId} = useParams(); const [product, setProduct] = useState({ ...props.product }); useEffect(() => { if (categories.length === 0) { getCategories(); } setProduct({ ...props.product }); }, [props.product]); function handleChange(event) { const { name, value } = event.target; setProduct((previousProduct) => ({ ...previousProduct, [name]: name === "categoryId" ? parseInt(value, 10) : value, })); } function handleSave(event) { event.preventDefault(); saveProduct(product).then(() => { history.push("/"); }); } return ( <ProductDetail product={product} categories={categories} onChange={handleChange} onSave={handleSave} ></ProductDetail> ); } export function getProductById(products, productId) { let product = products.find((product) => product.id == productId) || null; return product; } function mapStateToProps(state) { const productId = this.productId; const product = productId && state.productListReducer.length > 0 ? getProductById(state.productListReducer, productId) : {}; return { product, products: state.productListReducer, categories: state.categoryListReducer, }; } const mapDispatchToProps = { getCategories, getProducts, saveProduct, }; export default connect(mapStateToProps, mapDispatchToProps)(AddOrUpdateProduct);
App.js
import { Container } from "reactstrap"; import Navi from "../navi/Navi"; import Dashbord from "./Dashbord"; import { Routes, Route } from "react-router-dom"; import CartDetail from "../cart/CartDetail"; import AddOrUpdateProduct from "../products/AddOrUpdateProduct"; import NotFound from "../common/NotFound"; function App() { return ( <Container> <Navi></Navi> <Routes> <Route path="/" element={<Dashbord></Dashbord>}></Route> <Route path="/product" element={<Dashbord></Dashbord>}></Route> <Route path="/cart" element={<CartDetail></CartDetail>}></Route> <Route path="/saveproduct/:productId" element={<AddOrUpdateProduct/>} ></Route> <Route path="/saveproduct" element={<AddOrUpdateProduct/>} ></Route> <Route path ="*" element={<NotFound></NotFound>}></Route> </Routes> </Container> ); } export default App;
原因分析
mapStateToProps是Redux connect提供的函数,它的执行上下文和组件内部的Hooks完全隔离:既无法访问组件实例(所以this.productId完全无效),也拿不到useParams返回的路由参数。
解决方案
方案一:改用Redux Hooks替代connect(推荐)
React Redux v7.1+支持用useSelector和useDispatch替代connect,可直接在组件内结合useParams获取数据,逻辑更清晰:
import React, { useState, useEffect } from "react"; import { useSelector, useDispatch } from "react-redux"; import { saveProduct, getProducts } from "../../redux/actions/productActions"; import { getCategories } from "../../redux/actions/categoryActions"; import ProductDetail from "./ProductDetail"; import { useParams, useNavigate } from "react-router-dom"; function AddOrUpdateProduct() { const { productId } = useParams(); const navigate = useNavigate(); const dispatch = useDispatch(); // 从Redux获取列表数据 const products = useSelector(state => state.productListReducer); const categories = useSelector(state => state.categoryListReducer); // 根据productId筛选目标产品 const targetProduct = productId && products.length > 0 ? products.find(p => p.id == productId) || {} : {}; const [product, setProduct] = useState(targetProduct); useEffect(() => { if (categories.length === 0) { dispatch(getCategories()); } // 路由参数或产品列表变化时更新表单数据 setProduct(productId && products.length > 0 ? products.find(p => p.id == productId) || {} : {}); }, [productId, products, categories, dispatch]); function handleChange(event) { const { name, value } = event.target; setProduct((previousProduct) => ({ ...previousProduct, [name]: name === "categoryId" ? parseInt(value, 10) : value, })); } function handleSave(event) { event.preventDefault(); dispatch(saveProduct(product)).then(() => { navigate("/"); }); } return ( <ProductDetail product={product} categories={categories} onChange={handleChange} onSave={handleSave} ></ProductDetail> ); } export default AddOrUpdateProduct;
方案二:保留connect,在组件内处理数据筛选
如果坚持使用connect,可直接在组件内拿到productId后筛选数据,避开在mapStateToProps中访问路由参数:
// 修改AddOrUpdateProduct组件内部逻辑 function AddOrUpdateProduct({ products, categories, getProducts, getCategories, saveProduct, history, }) { let { productId } = useParams(); // 直接在组件内根据productId获取目标产品 const targetProduct = productId && products.length > 0 ? products.find(p => p.id == productId) || {} : {}; const [product, setProduct] = useState(targetProduct); useEffect(() => { if (categories.length === 0) { getCategories(); } // 路由参数或产品列表变化时更新表单数据 setProduct(productId && products.length > 0 ? products.find(p => p.id == productId) || {} : {}); }, [productId, products, categories]); // ...其余handleChange、handleSave逻辑不变 } // 修改mapStateToProps,仅返回基础列表数据 function mapStateToProps(state) { return { products: state.productListReducer, categories: state.categoryListReducer, }; } // 其余connect代码不变
内容的提问来源于stack exchange,提问作者Leonidas
相关产品推荐
相关产品推荐

