如何用Redux结合数组单独管理每个商品的点赞状态?
问题描述
我尝试使用Redux和map函数实现like功能,以便在其他组件中使用状态。选择Redux而非useState([])是因为认为它更适合状态管理。目前点击按钮时所有商品的状态会同步变化,因为它们共享同一状态。我考虑将状态存入数组来解决该问题,想请教如何在Redux中添加状态以单独管理每个商品的点赞状态,以及这种方式是否正确。
以下是Clothes组件的代码:
import React from "react"; import styled from "styled-components"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faHeart } from "@fortawesome/free-solid-svg-icons"; import { faHome } from "@fortawesome/free-solid-svg-icons"; import { useDispatch, useSelector } from "react-redux"; const array = [ { id: "1", name: "vest", img: "https://shopimg.kakaofriendsgolf.com/live/images/2022/9/7/10/918997_1662515279620.png", price: "10000" }, { id: "2", name: "shirts", img: "https://shopimg.kakaofriendsgolf.com/live/images/2022/8/23/18/551886_1661246483199.png", price: "12000" }, { id: "3", name: "pants", img: " https://shopimg.kakaofriendsgolf.com/live/images/2022/8/22/18/18783_1661159105201.png", price: "15000" } ]; export default function Clothes() { const isClick = useSelector((state) => state.CheckLike.isClick); const dispatch = useDispatch(); // change Icon const setHide = (e) => { dispatch({ type: "False" }); }; const setShow = (e) => { dispatch({ type: "True" }); }; return ( <Wrap> <div className="productCard"> {array.map((content, idx) => { return ( <div key={idx} className="productCard__wrap"> <img src={content.img} className="productCard__img" /> <div className="productCard__name"> <div> <h3 className="productCard__title">{content.name}</h3> <div className="productCard__priceWrap"> {content.price .toString() .replace(/\B(?=(\d{3})+(?!\d))/g, ",")} </div> </div> <div className="productCard__likeWrap"> {/* show heart and house icon according to state */} {isClick ? ( <div onClick={() => { setHide(); }} > <FontAwesomeIcon icon={faHeart} /> </div> ) : ( <div onClick={() => { setShow(); }} > <FontAwesomeIcon icon={faHome} /> </div> )} </div> </div> </div> ); })} </div> </Wrap> ); } const Wrap = styled.div` position: fixed; top: 0; bottom: 0; left: 0; right: 0; overflow: scroll; .productCard { display: flex; position: absolute; top: calc(50vh - 120px); left: calc(50vw - 180px); width: "228px"; height: "351px"; padding-right: 8px; padding-left: 8px; border: solid 1px black; } .productCard__wrap { border: 1px solid grey; line-height: 1.428571429; background-color: #fff; } .productCard__img { width: 228px; height: 228px; } .productCard__name { padding-bottom: 30px; position: relative; padding: 8px 8px 0 8px; min-height: 118px; text-align: left; } .productCard__title { font-size: 18px; margin-top: 0; margin-bottom: 2px; padding-right: 37px; line-height: 1.3; margin-block-start: 1em; margin-block-end: 1em; } .productCard__priceWrap { font-size: 18px; margin-top: 5px; vertical-align: middle; margin: 0 0 9px; margin-block-start: 1em; margin-block-end: 1em; text-align: left; } .productCard__likeWrap { position: absolute; top: -5px; right: 0; padding: 0; height: 40px; line-height: 37px; white-space: normal; display: inline-block; margin-bottom: 0; font-weight: normal; text-align: center; } `;
解决方案
你的思路是对的——用数组或更高效的对象映射来存储每个商品的点赞状态,而非单个布尔值,这样就能单独控制每个商品的点赞状态。具体实现步骤如下:
1. 修改Redux状态结构
把原来单一的isClick布尔值,改成以商品ID为键、布尔值为值的对象(比数组查询更新效率更高):
// 初始状态示例 const initialState = { likedIds: {} // 格式示例:{ "1": true, "2": false } };
2. 更新Reducer逻辑
修改Reducer,处理单个商品的点赞/取消点赞动作,接收商品ID作为payload:
const checkLikeReducer = (state = initialState, action) => { switch (action.type) { case "TOGGLE_LIKE": const productId = action.payload; return { ...state, likedIds: { ...state.likedIds, [productId]: !state.likedIds[productId] // 切换该商品的点赞状态 } }; default: return state; } };
3. 调整Clothes组件逻辑
- 不再使用全局的
isClick,而是根据当前商品ID从Redux状态中获取对应点赞状态 - 点击时dispatch带商品ID的动作,而非统一的True/False指令
修改后的核心组件代码:
export default function Clothes() { const likedIds = useSelector((state) => state.CheckLike.likedIds); const dispatch = useDispatch(); // 切换单个商品的点赞状态 const toggleLike = (productId) => { dispatch({ type: "TOGGLE_LIKE", payload: productId }); }; return ( <Wrap> <div className="productCard"> {array.map((content) => { // 根据商品ID判断是否已点赞 const isLiked = likedIds[content.id] || false; return ( <div key={content.id} className="productCard__wrap"> <img src={content.img} className="productCard__img" /> <div className="productCard__name"> <div> <h3 className="productCard__title">{content.name}</h3> <div className="productCard__priceWrap"> {content.price .toString() .replace(/\B(?=(\d{3})+(?!\d))/g, ",")} </div> </div> <div className="productCard__likeWrap"> <div onClick={() => toggleLike(content.id)}> <FontAwesomeIcon icon={isLiked ? faHeart : faHome} /> </div> </div> </div> </div> ); })} </div> </Wrap> ); }
方案合理性说明
- 每个商品的点赞状态独立存储,不会出现同步变化的问题
- Redux状态结构清晰,其他组件可以轻松获取任意商品的点赞状态
- 用对象存储
likedIds,更新和查询的时间复杂度均为O(1),比数组遍历更高效 - 改用商品唯一ID作为列表
key,符合React列表渲染的最佳实践,避免组件复用导致的状态混乱
内容的提问来源于stack exchange,提问作者DgUZH
相关产品推荐
相关产品推荐

