为API获取的数组对象添加isAdded后点击按钮时该值显示undefined求助
问题描述
从API获取商品数据后,通过forEach循环给商品数组里的每个对象添加isAdded键并设初始值为false,商品添加到<Cart />组件的功能正常,但点击“Add to cart”按钮时,isAdded值显示undefined,无法实现按钮文本和样式的切换。
相关代码
BookProduct.js
import BookItems from "./BookItems"; import classes from "./book-item.module.css"; import { useEffect } from "react"; export default function BookProducts({ booksData }) { useEffect(() => { booksData.forEach((element) => { element.isAdded = false; }); }, []); console.log(booksData); // isAdded键已成功添加到对象中 return ( <div className="container"> {booksData.map((el) => { return ( <BookItems id={el.isbn13} key={el.isbn13} imageUrl={el.image} price={Number(el.price.slice(1))} title={el.title} subtitle={el.subtitle} link={el.link} isAdded={el.isAdded} /> ); })} </div> ); }
BookItems.js
import { useDispatch, useSelector } from "react-redux"; import Image from "next/image"; import Link from "next/link"; import classes from "./book-item.module.css"; import { cartStoreAction } from "../../store/cart-items"; export default function BookItems(props) { const { id, title, price, imageUrl, isAdded } = props; const dispatch = useDispatch(); const addToCartHandler = () => { dispatch( cartStoreAction.addProduct({ id, title, price, imageUrl, isAdded }) ); console.log(isAdded); // 此处isAdded显示undefined }; return ( <div className={classes.product__item}> <Link href={`/products/${id}`} className={classes.image_box}> <Image src={imageUrl} alt={title} className={classes.image} fill /> </Link> <div className={classes.desc_box}> <Link href={`/products/${id}`}> <h3>{title}</h3> </Link> <div className={classes.bottom}> <p>${price}</p> <button onClick={addToCartHandler}> {isAdded ? '已添加' : '加入购物车'} </button> </div> </div> </div> ); }
cart-items.js
const cartItemSlice = createSlice({ name: "cart", initialState: { items: [], totalQuantity: 0, totalAmount: 0, }, reducers: { addProduct(state, action) { const newCartItems = action.payload; // 来自dispatch的对象 const existingItem = state.items.find((el) => el.id === newCartItems.id); state.totalQuantity++; if (!existingItem) { state.items.push({ id: newCartItems.id, quantity: 1, title: newCartItems.title, price: newCartItems.price, image: newCartItems.imageUrl, totalPrice: newCartItems.price, isAdded: !newCartItems.isAdded, }); !newCartItems.isAdded; } else { existingItem.quantity++; existingItem.totalPrice = existingItem.totalPrice + newCartItems.price; } state.totalAmount = state.items.reduce( (acc, index) => acc + Number(index.price) * Number(index.quantity), 0 ); }, }, });
问题原因及修复方案
核心问题
- 渲染时机不匹配:
useEffect在组件首次渲染后执行,第一次渲染BookItems时,el.isAdded还未被添加,导致props.isAdded为undefined;后续即使修改了booksData,也没有触发组件重渲染,BookItems接收的isAdded始终是初始的undefined。 - props修改反模式:直接修改传入的
booksData(props)不符合React规范,props是只读的,修改不会触发重渲染。 - 无效状态更新逻辑:Redux reducer里的
!newCartItems.isAdded;是无效语句,无法同步更新组件的isAdded状态。
修复步骤
步骤1:用本地state管理商品数据
在BookProducts中将booksData转为本地state,确保添加isAdded后触发重渲染:
import BookItems from "./BookItems"; import classes from "./book-item.module.css"; import { useEffect, useState } from "react"; export default function BookProducts({ booksData }) { const [products, setProducts] = useState([]); useEffect(() => { // 创建新数组,不修改原props,给每个商品添加isAdded初始值 const updatedProducts = booksData.map(element => ({ ...element, isAdded: false })); setProducts(updatedProducts); }, [booksData]); // 依赖booksData,数据更新时重新处理 return ( <div className="container"> {products.map((el) => { return ( <BookItems id={el.isbn13} key={el.isbn13} imageUrl={el.image} price={Number(el.price.slice(1))} title={el.title} subtitle={el.subtitle} link={el.link} isAdded={el.isAdded} // 添加更新isAdded的回调函数 updateIsAdded={(id) => { setProducts(prev => prev.map(item => item.isbn13 === id ? {...item, isAdded: !item.isAdded} : item )); }} /> ); })} </div> ); }
步骤2:修改BookItems组件,调用回调更新状态
import { useDispatch } from "react-redux"; import Image from "next/image"; import Link from "next/link"; import classes from "./book-item.module.css"; import { cartStoreAction } from "../../store/cart-items"; export default function BookItems(props) { const { id, title, price, imageUrl, isAdded, updateIsAdded } = props; const dispatch = useDispatch(); const addToCartHandler = () => { dispatch( cartStoreAction.addProduct({ id, title, price, imageUrl, isAdded: !isAdded // 传递切换后的值到Redux }) ); // 调用父组件回调更新本地isAdded状态 updateIsAdded(id); }; return ( <div className={classes.product__item}> <Link href={`/products/${id}`} className={classes.image_box}> <Image src={imageUrl} alt={title} className={classes.image} fill /> </Link> <div className={classes.desc_box}> <Link href={`/products/${id}`}> <h3>{title}</h3> </Link> <div className={classes.bottom}> <p>${price}</p> <button onClick={addToCartHandler}> {isAdded ? '已添加' : '加入购物车'} </button> </div> </div> </div> ); }
步骤3:清理Redux reducer无效代码
移除reducer里的无效语句,且购物车状态无需存储isAdded(按钮状态属于组件本地UI状态):
const cartItemSlice = createSlice({ name: "cart", initialState: { items: [], totalQuantity: 0, totalAmount: 0, }, reducers: { addProduct(state, action) { const newCartItems = action.payload; const existingItem = state.items.find((el) => el.id === newCartItems.id); state.totalQuantity++; if (!existingItem) { state.items.push({ id: newCartItems.id, quantity: 1, title: newCartItems.title, price: newCartItems.price, image: newCartItems.imageUrl, totalPrice: newCartItems.price, }); } else { existingItem.quantity++; existingItem.totalPrice = existingItem.totalPrice + newCartItems.price; } state.totalAmount = state.items.reduce( (acc, index) => acc + Number(index.price) * Number(index.quantity), 0 ); }, }, });
内容的提问来源于stack exchange,提问作者Mohit
相关产品推荐
相关产品推荐

