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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 23:50:28