React-Redux Hooks调用顺序异常报错及商品渲染问题求助
问题修复方案
1. 解决Hook调用顺序错误
错误原因是你在CatalogProducts中直接以函数调用的方式渲染ProductCard,而非React组件的JSX标签形式。这种写法会导致ProductCard内部的useDispatch Hook在循环中被调用,违反了Hook必须在组件顶层按固定顺序调用的规则。
修改代码:
- CatalogProducts组件:将函数调用改为JSX组件渲染,传递props:
const CatalogProducts = () => { const newCatalog = useSelector((state) => state) return ( <div> {newCatalog.catalog.map((prod, index) => ( <ProductCard key={index} product={prod} index={index} /> ))} </div> ); };
- ProductCard组件:接收props对象(React组件的参数是单个props对象,不是分开的参数),同时修正变量名规范:
const ProductCard = ({ product, index }) => { const dispatch = useDispatch() function deleteProd() { dispatch(removeProductAction(index)) } return ( <Card style={{ width: '18rem'}} className="m-2" key={index}> <Card.Body className="bg-success text-white card" border="primary"> <img src={product.imgSrc} alt={'product'}/> <Card.Subtitle className="mb-2" style={{ fontSize: 16 }}>Name: {product.name}</Card.Subtitle> <Card.Subtitle className="mb-2" style={{ fontSize: 14 }}>Info: {product.infoProd}</Card.Subtitle> <Card.Subtitle className="mb-2">Price: {product.price} $</Card.Subtitle> <Card.Subtitle className="mb-2" style={{ fontSize: 10 }}>{product.date}</Card.Subtitle> <Button variant="danger" onClick={deleteProd}>delete</Button> </Card.Body> </Card> ); };
2. 修复Reducer状态更新问题(解决页面不同步)
你的Reducer中使用了push和splice方法,这两个方法会直接修改原数组,违反了Redux状态不可变的原则;同时你错误地将state.catalog赋值为这些方法的返回值(push返回数组长度,splice返回删除的元素),导致状态结构被破坏,组件无法感知到状态变化。
修改productsReducer:
function productsReducer (state, action) { switch (action.type){ case ADDPRODUCTS: // 创建新数组,添加新元素,保持原状态不变 return { ...state, catalog: [...state.catalog, action.payload] } case REMOVEPRODUCTS: // 用filter创建排除指定索引的新数组 return { ...state, catalog: state.catalog.filter((_, i) => i !== action.payload) } default: return state } }
3. 优化CreateProduct组件的表单管理(可选但推荐)
你当前直接修改newProduct对象的属性,虽然能工作,但推荐用useState管理表单状态,避免组件重渲染时的意外问题,同时修正输入框placeholder与绑定属性不匹配的问题:
import React, { useState } from 'react'; import {useDispatch} from "react-redux"; import {addProductAction} from "../action/productsAction"; import {ConfigProduct} from "../Utils/configProduct"; import '../css/popup.css' import '../css/flex.css' import {Button} from "react-bootstrap"; const CreateProduct = () => { const dispatch = useDispatch() const [productData, setProductData] = useState({ idProd: '', name: '', infoProd: '', price: '', date: '', imgSrc: '' }) function addProd() { const newProduct = new ConfigProduct( productData.idProd, productData.name, productData.infoProd, productData.price, productData.date, productData.imgSrc ) dispatch(addProductAction(newProduct)) // 清空表单 setProductData({ idProd: '', name: '', infoProd: '', price: '', date: '', imgSrc: '' }) } return ( <div className="popup"> <h2 style={{textAlign: "center", color: "red"}}>New product</h2> <input className="item" placeholder="id" value={productData.idProd} onChange={e => setProductData(prev => ({...prev, idProd: e.target.value}))} /> <input className="item" placeholder="name" value={productData.name} onChange={e => setProductData(prev => ({...prev, name: e.target.value}))} /> <input className="item" placeholder="info" value={productData.infoProd} onChange={e => setProductData(prev => ({...prev, infoProd: e.target.value}))} /> <input className="item" placeholder="price" value={productData.price} onChange={e => setProductData(prev => ({...prev, price: e.target.value}))} /> <input className="item" placeholder="date" value={productData.date} onChange={e => setProductData(prev => ({...prev, date: e.target.value}))} /> <input className="item" placeholder="enter url" value={productData.imgSrc} onChange={e => setProductData(prev => ({...prev, imgSrc: e.target.value}))} /> <p> <Button onClick={addProd}>Add</Button> </p> </div> ); }; export default CreateProduct;
内容的提问来源于stack exchange,提问作者Aleksandr
相关产品推荐
相关产品推荐

