You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 06:57:09