0

在 Reconciliation 的 ReactJS 页面中,有两个例子:

  1. 使用索引作为键可能导致的问题的示例
  2. 同一示例的更新版本,展示了如何不使用索引作为键来解决这些重新排序、排序和前置问题

截至 2020 年 2 月 18 日,尚不清楚如何在页面上重现该问题。我尝试了几次单击“添加新的开始”或“结束”并重新排序列表,它们似乎工作正常。直到后来我才发现你需要在框中输入一些文本,然后只需“Add New to End”,然后重复三遍,然后重新排列列表。

在第一个示例中,输入框中的文本没有重新排序。在第二个示例中,输入框中的文本按预期重新排序。

这两个程序的不同之处在于使用

<ToDo key={index} {...todo} />

对比

<ToDo key={todo.id} {...todo} />

在两个版本之间还有一些稍微重新排序的代码和使用todoCountervs toDoCounter(capital D),我想知道为什么,React 团队可能会在以后修复它。但是您可以将第一个版本从key={index}to修改key={todo.id},您也可以看到问题已解决。

但是当我查看代码时,输​​入框实际上并没有将文本数据添加到状态属性list(数组)中。只有idcreatedAt被添加到每个新条目list

因此,虽然我们可以说使用key={todo.id}解决了问题,但首先是什么导致了问题?

您可以说idandcreatedAt已正确排序。没有正确排序的是输入框...但是根据和解规则决定是否刷新页面上的实际DOM元素,新的Virtual DOM子树与之前的虚拟DOM子树进行比较。

现在输入框的“属性”value不是虚拟 DOM 的一部分,除非 React 实际上默默地把它value放入虚拟 DOM 中。

所以当递归地“diff'ing”时,React 应该认为输入框都是一样的。这就是它的工作原理:如果我们使用key={index},现在 React 将当前虚拟 DOM 子树中的每一列与前一列进行比较,并看到“ID”和“createAt”单元格不同,因此强制刷新实际的 DOM。React 看到的输入框都是一样的,并没有费心去强制刷新实际的 DOM。

但是,如果我们使用key={todo.id},现在 React 会认为,整是不同的,因为行“id”已经改变。因此 React 将强制刷新整行的实际 DOM,包括输入框。

所以我们可以说,只有当某些数据不在虚拟 DOM 子树中时才会出现此错误……这很少见,例如在当前情况下。在其他情况下,所有数据都将由render()类组件或return函数组件提供,因此能够告诉 ReactJS,“是的,强制刷新到实际的 DOM”。

这真的是这样吗?

4

1 回答 1

0

使用索引作为键是非常危险的,强烈建议不要使用。

例如,如果您从列表中间删除一个项目,另一个项目将取而代之,React 需要一个唯一的键来将 DOM 元素识别为单个/独立元素,而不是临时或无用的元素。

您的列表正在按预期排序,您可以看到 ID 列已准确排序。

但是在您的情况下,React 不知道哪个 todo 属于哪个 ID,索引不是 todo 列表 ID,它们是按顺序生成的数字。

想象一下

Todos:
id 1
todo: "take out the trash"

id 2
todo: "make dinner"

if you sort your todos by the shortest string
it will end up like this:
2
1

正常顺序是:

1
2

无论您如何对列表进行排序并更改其顺序,如果您告诉 React 使用索引,它总是会像这样呈现它:

1
2
3
4
...etc

如果您使用对象 ID,React 将跟踪元素并将键连接到 ID,然后根据排序 ID 的顺序呈现您的元素。

于 2020-02-19T01:51:38.123 回答