【问题标题】:Why is removing an item from a list with the key as index working as expected?为什么使用键作为索引从列表中删除项目按预期工作?
【发布时间】:2020-12-30 12:36:43
【问题描述】:

我有一个组件,您可以在其中添加或删除一个选项项。当我删除一个选项项时,它只是从使用index 值存储在状态中的options 列表中删除。

我本以为因为我使用 index 作为键,所以每当我删除一个选项项时,最后一个元素会被错误地删除,但它似乎按预期工作。

   displayedOptions = options.map((option, optionIndex) => (
      <DropdownOption
        option={option}
        onRemoveOption={() => onRemoveOption(optionIndex)}
        onChange={onChangeOption(optionIndex)}
        key={optionIndex}
      />
    ));
  const onRemoveOption = (taskIndex: number) => (optionIndex: number) => {
    const newTaskFields = [...taskFields];
    newTaskFields[taskIndex].options = newTaskFields[taskIndex].options.filter(
      (_option, index) => {
        return optionIndex !== index;
      }
    );
    setTaskFields(newTaskFields);
  };

这样做有什么风险吗? 有人知道为什么会按预期工作吗?

我认为我的应用中的行为与此处报告的行为相似:React - Deleting from middle of list removes last element instead

也就是说,如果我有一个使用索引作为包含以下值的键的列表:

[a, b, c]

并且我删除了索引 0 ('a'),我认为差异会在:

Original:
[0:a, 1:b, 2:c]
and
Updated:
[0:b, 1:c]

在这种情况下,React 会看到键 01 仍然存在,并将继续渲染 ab,因为它会假设这些键没有改变。这将导致c 消失(而不是a)。

谢谢。

【问题讨论】:

  • 好吧,大概如果按索引删除删除处理程序正确过滤了该索引处元素 not 的选项,那么我希望它能够按预期工作。但是,我们看不到您的 onRemoveOption 代码。您的问题真的是“我的代码有效,但我不知道为什么吗?”
  • @DrewReese 我已经包含了onRemoveOption。是的,这本质上是我的问题。
  • 是的,你创建一个状态的浅拷贝,改变它(删除与索引匹配的元素)并更新状态。看起来大部分都符合我的预期。首先缺少的是newTaskFields[taskIndex] 的浅拷贝,但我想这不会给你带来问题;这是状态突变的可能性。

标签: reactjs


【解决方案1】:

在实现基于地图索引的排序和删除时会出现问题。

例如

让选项 = [d,a,e,y];

现在选项和索引看起来像这样。

d 0 一个 1 2 是 3

删除项目始终应用对上一个项目的更改。因为您没有将唯一键传递给该项目。读取 diff 算法。

但是如果你实现排序和删除 - 它会在删除特定元素时失败

排序后索引会改变 一个 0 d 1 2 是 3

您可以发送选项名称而不是索引。

【讨论】:

  • 感谢 Vsquare 但是为什么我没有看到最后一项消失,即使我正在使用索引?我在最后更新了我的问题并提供了一些细节
【解决方案2】:

您的onRemoveOption 部分正确。您应该浅层复制您打算更新的数组/对象的所有元素(变异/删除/等...)。

您的 onRemoveOption 处理程序将变为:

const onRemoveOption = (taskIndex: number) => (optionIndex: number) => {
  setTaskFields((tasks) =>
    tasks.map((task, index) =>
      index === taskIndex
        ? {
            ...task,
            options: task.options.filter((_, index) => index !== optionIndex)
          }
        : task
    )
  );
};
  • 使用功能状态更新
  • 将任务映射到新的数组对象
  • 如果任务索引与您需要从中删除选项的索引匹配,则浅拷贝到新对象并更新options 属性,否则返回任务对象
  • 如果删除选项,请按索引过滤选项

【讨论】:

  • 谢谢 Drew,但你的意思是说你应该浅拷贝我们不打算更新的数组/对象的所有元素吗?因为您只是返回我们没有更新的task
  • 我还更新了我的问题以解释我对索引的疑问
  • @Bleh 不,您正在更新数组中的一个元素,因此您需要将所有元素浅拷贝到一个新数组中,并且由于您正在更新特定元素的 options 属性,所以浅浅将属性复制到新对象中并更新 options 属性。没有更新其选项的元素被简单地传递给新数组。 codesandbox
猜你喜欢
  • 2020-04-18
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-30
  • 2014-08-04
  • 2021-01-03
相关资源
最近更新 更多