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

动态输入删除后新增时已删项重现,key含空格是否影响?

动态输入框删除后新增异常的问题解决

1. 用配料名称当key确实是核心问题之一

React依赖key识别列表组件,确保正确复用和更新。如果用ing.name作为key:

  • 一旦出现名称重复的配料,key会重复,React会把它们当成同一个组件处理,删除后新增时直接复用旧组件状态,导致刚删除的项“重新出现”。
  • 就算名称不重复,删除操作改变数组顺序后,索引对应的key和组件映射关系会混乱,React的diff算法无法正确识别组件增删,反而复用旧组件引发显示异常。

解决方法:给每个配料项添加唯一ID,比如用时间戳生成,修改state结构为包含唯一标识的对象:

// 初始化state
const [savedingredients, setIngredients] = useState([]);

// 新增时添加唯一id
const saveIngredient = async () => {
  const name = document.getElementById("ing_name1").value;
  reset({ ...getValues(), ing_name1: "" });
  // 用Date.now()生成唯一id,也可以用uuid库
  const newIngredient = { id: Date.now(), name: name };
  setIngredients([...savedingredients, newIngredient]);
};

渲染时用ing.id作为key:

<ListItem key={ing.id}>

2. 直接修改原state数组导致状态更新异常

在saveIngredient里,var ingredients_array = savedingredients;是直接引用原state数组,然后push修改原数组——这违反了React状态不可变的原则,会导致React无法检测到状态变化,即使后面创建了新数组,原state已经被污染,容易引发不可预测的渲染问题。

解决方法:始终创建原数组的副本再修改,避免直接操作原state:

// 错误写法(直接修改原state)
// var ingredients_array = savedingredients;
// ingredients_array.push({ name: name });

// 正确写法(创建副本后修改)
const newArray = [...savedingredients, { id: Date.now(), name: name }];
setIngredients(newArray);

3. 删除逻辑依赖索引的隐患

当前deleteIngredient用索引过滤项,结合key不唯一的问题,会导致数组变化后索引对应的项和预期不符。改成用唯一ID删除更可靠:

const deleteIngredient = (itemId) => {
  // 用filter过滤掉对应id的项,比map更简洁
  const updatedIngredients = savedingredients.filter(ing => ing.id !== itemId);
  setIngredients(updatedIngredients);
  enqueueSnackbar("Ingredient Deleted", { variant: "success" });
};

调用时传入当前项的id:

<IconButton onClick={() => deleteIngredient(ing.id)}>

4. React Hook Form的Controller名称优化

当前用name={ing${i + 1}},当数组项删除后,后续项的索引变化会导致name改变,和react-hook-form的表单状态不匹配。改成用唯一ID作为name,确保每个输入框的标识唯一:

<Controller
  name={`ing_${ing.id}`} // 用唯一id作为name
  defaultValue={ing.name}
  control={control}
  rules={{ required: true, minLength: 3 }}
  render={({ field }) => (
    <TextField
      variant="outlined"
      fullWidth
      id={`ing_${ing.id}`}
      inputProps={{ type: "text", disabled: true }}
      {...field}
    />
  )}
/>

修改后的核心代码片段

import { useState } from 'react';

const [savedingredients, setIngredients] = useState([]);

const saveIngredient = async () => {
  const name = document.getElementById("ing_name1").value;
  reset({ ...getValues(), ing_name1: "" });
  const newIngredient = { id: Date.now(), name: name };
  setIngredients([...savedingredients, newIngredient]);
};

const deleteIngredient = (itemId) => {
  const updatedIngredients = savedingredients.filter(ing => ing.id !== itemId);
  setIngredients(updatedIngredients);
  enqueueSnackbar("Ingredient Deleted", { variant: "success" });
};

// 渲染部分
<List>
  {savedingredients.map((ing) => (
    <ListItem key={ing.id}>
      <Grid container spacing={1}>
        <Grid item xs={6} md={6}>
          <Controller
            name={`ing_${ing.id}`}
            defaultValue={ing.name}
            control={control}
            rules={{ required: true, minLength: 3 }}
            render={({ field }) => (
              <TextField
                variant="outlined"
                fullWidth
                id={`ing_${ing.id}`}
                inputProps={{ type: "text", disabled: true }}
                {...field}
              />
            )}
          />
        </Grid>
        <Grid item xs={3} md={2}>
          <IconButton
            onClick={() => deleteIngredient(ing.id)}
            aria-label="close"
            sx={{ position: "absolute", right: 8, top: 8, color: (theme) => theme.palette.grey[500] }}
            edge="end"
          >
            <CloseIcon />
          </IconButton>
          <IconButton
            onClick={(e) => editIngredient(e, ing.id)} // 编辑逻辑也建议用id而非索引
            aria-label="toggle action"
            sx={{ position: "absolute", right: 30, top: 8, color: (theme) => theme.palette.grey[500] }}
            edge="start"
          >
            {stepAction == "Edit" ? <Edit /> : <Save />}
          </IconButton>
        </Grid>
      </Grid>
    </ListItem>
  ))}
</List>

内容的提问来源于stack exchange,提问作者Faiza

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 02:05:27