React中如何渲染数组全部值?购物列表显示异常求助
React购物清单仅显示首个元素的问题解决
我刚接触React,尝试基于包含食材数组的食谱对象列表创建购物清单,但React只显示第一个元素,剩下的元素得每次点击按钮才会显示。我的代码如下:
import { useState, useContext } from 'react'; import { MealContext } from '../../../Context/MealContext'; import GroceryListComponent from './GroceryListComponent'; const GrocerysListContainer = () => { const [selectedMeals] = useContext(MealContext); const [groceryList, setGroceryList] = useState([]); const [showList, setShowList] = useState(false); const handleClick = () => { setShowList(true); selectedMeals.forEach(meal => { meal.ingredient.forEach((dish) => { if (!groceryList.find(({name}) => name === dish.toLowerCase())) { setGroceryList([...groceryList, {name : dish.toLowerCase(), qty : 1}]); console.log('add') } else { console.log('remove') return; } }) }); } return ( <div className="groceryList"> <button className="btn btn-submit" onClick={handleClick}>Creer liste de course</button> {showList && <ul> {groceryList.map((grocery, index) => { return ( <GroceryListComponent key={index} grocery={grocery} /> ) })} </ul>} </div> ); }; export default GrocerysListContainer;
问题原因
核心问题在于handleClick里的状态更新逻辑:React的setState是异步操作,循环中每次调用setGroceryList时,你都在使用当前函数作用域里的旧状态值,而非最新的状态。所有循环内的setGroceryList调用都基于初始的空数组,最终只会保留单次更新的结果,导致每次点击只能添加一个元素。
解决方案
方式1:使用函数式更新状态
setState支持传入函数,该函数会接收当前最新状态作为参数,返回新状态。这样每次更新都基于最新值:
const handleClick = () => { setShowList(true); selectedMeals.forEach(meal => { meal.ingredient.forEach((dish) => { const lowerDish = dish.toLowerCase(); setGroceryList(prevList => { if (!prevList.find(item => item.name === lowerDish)) { console.log('add'); return [...prevList, { name: lowerDish, qty: 1 }]; } else { console.log('已存在'); return prevList; // 不修改状态 } }); }); }); }
方式2:先处理所有食材,一次性更新状态
更高效的方式是先把所有需要添加的食材整理成完整数组,再调用一次setGroceryList,避免多次状态更新:
const handleClick = () => { setShowList(true); const newGroceryList = [...groceryList]; selectedMeals.forEach(meal => { meal.ingredient.forEach((dish) => { const lowerDish = dish.toLowerCase(); if (!newGroceryList.find(item => item.name === lowerDish)) { newGroceryList.push({ name: lowerDish, qty: 1 }); console.log('add'); } else { console.log('已存在'); } }); }); setGroceryList(newGroceryList); }
额外优化点
- 原代码中
useContext解构的setMeals未使用,可简化为const [selectedMeals] = useContext(MealContext); - 提前缓存
dish.toLowerCase()的结果,避免重复计算 forEach中的return无法中断循环,直接跳过即可
内容的提问来源于stack exchange,提问作者Dk38
相关产品推荐
相关产品推荐

