React 中重复添加已删除项失效的解决方案

react 列表操作中,因 `

在 React 中, 的当前值仍为 "Chicken Breast",此时再次点击同一选项,浏览器不会触发 onChange,id 变量也不会更新——导致后续 Add 按钮逻辑中 id === food[i].name 始终为 false,新增失败。

✅ 正确做法:使用受控组件 + 清空 select 值

import foods from 'json/foods';

const [meal, setMeal] = useState([]);
const [selectedFoodName, setSelectedFoodName] = useState(''); // ✅ 受控状态

const handleAdd = () => {
  if (!selectedFoodName) return;

  const foodToAdd = foods.find(food => food.name === selectedFoodName);
  if (!foodToAdd) return;

  // 防重:检查是否已存在同名项(推荐用 id 判断更可靠)
  if (meal.some(item => item.id === foodToAdd.id)) {
    alert(`"${foodToAdd.name}" 已在列表中`);
    return;
  }

  setMeal(prev => [...prev, foodToAdd]);
  setSelectedFoodName(''); // ✅ 添加后立即清空 select
};

const handleRemove = (id) => {
  setMeal(prev => prev.filter(item => item.id !== id));
};

对应 JSX 更新如下:

  {/* 渲染列表 */}
  {meal.map((item) => (
    
      

{item.name}

Protein: {item.protein}

Fats: {item.fats}

Carbs: {item.carbs}

handleRemove(item.id)} /> ))} {/* 受控 select */}

⚠️ 注意事项与最佳实践

  • 优先用 id 而非 name 去重:食物名称可能重复(如不同品牌的“Oatmeal”),而 id 是唯一标识,更健壮;
  • 避免全局变量 id:原代码中 let id; 是模块级变量,易引发闭包和竞态问题,必须用 useState 管理;
  • 不要直接修改 state 数组:meal.concat(...) 虽可用,但推荐使用函数式更新 setMeal(prev => [...prev, newItem]),确保基于最新状态;
  • key 必须稳定唯一:确保 foods 和 meal 数据中的 id 字段真实唯一且不可变,否则 React 渲染会出现异常。

通过受控组件 + 显式重置,你不仅能解决“删后无法重加”的问题,还能让数据流更清晰、可预测,符合 React 的单向数据流设计哲学。