为啥提供 key=index 会导致与不定义键不同的行为? [关闭]

Posted

技术标签:

【中文标题】为啥提供 key=index 会导致与不定义键不同的行为? [关闭]【英文标题】:Why does providing key=index result in different behavior than defining no key? [closed]为什么提供 key=index 会导致与不定义键不同的行为? [关闭] 【发布时间】:2021-08-27 21:26:45 【问题描述】:

如果我们定义 key=index确实 工作,如果我们排除它, 工作,但我希望基于文档的行为是相同的。

According to the docs:

如果您选择不为列表项分配显式键,那么 React 将默认使用索引作为键。

为什么会这样?不提供密钥不应该等同于提供key=index吗?

【问题讨论】:

如果您从链接向下滚动一点,就会看到指向an in-depth explanation about why keys are necessary的链接 您能否提供一个最小可重现的行为差异示例? React 使用 它自己的内部索引,它基于省略键时它需要构建的虚拟 DOM。此外,永远不要使用迭代索引作为键,使用实际唯一的元素值作为键。关键在于,key 唯一地标识了一组元素中的 那个元素,以便 React 能够正确地完成它的工作。 这不是“正常工作”的定义。 React 浪费时间不断地拆除和重建相同的数据,浪费 CPU 时间、时钟时间、内存,不必要地消耗移动设备上的电池等,因此根本无法正常工作。它可以工作,但不能正常工作,而且它当然不是你应该如何使用 React。你得到了所有这些强烈反对,因为你的问题没有遵循a good question 的一般形式:解释你在做什么,你期望什么(使用那个文档链接:这是一个很好的链接),以及你得到了什么. 好的,所以在 4 人引用文档说它“不好”,6 人投反对票之后,我仍然很好奇:如果没有密钥,React 会做什么?老实说,我不知道,我希望看到这个问题的真正答案。 【参考方案1】:

文档措辞不当:React does not actually use a key 如果您没有指定一个,并且省略 key 属性与使用 key=index 相同。相反,如果不存在 key 属性,React 将基于其虚拟 DOM 父元素中元素的子索引进行内部检查。这些可能(并且通常会)与您的迭代索引相同,但肯定并非总是如此,并且依赖它们相同会导致错误。

作为“迭代索引”和“虚拟 DOM 子索引”不同时的示例,让我们看一下从多个输入列表创建子元素的情况。如果我们不指定 key 属性,React 会警告缺少键,但一切都会按预期工作:

class App extends Component 
  constructor(props) 
    super(props);  
    this.state = 
      a: [1,2,3],
      b: [4,5,6]
   ;
  
  render() 
    const  a, b  = this.state;
    return <div>
      a.map(e => <p>e</p>).concat(b.map(e => <p>e</p>))
    div>;
  

我们得到了六个无键段落,内容从 1 到 6,正如您想象的那样,我们只需查看代码即可。

但是,如果我们按照 React 的建议添加 key 属性,但我们错误地使用数组索引作为键,something you should never do,事情就会停止按预期工作:

class App extends Component 
  constructor(props) 
    super(props);  
    this.state = 
      a: [1,2,3],
      b: [4,5,6]
   ;
  
  render() 
    const  a, b  = this.state;
    return <div>
      a.map((e,index) => <p key=index>e</p>)
       .concat(b.map((e, index) => <p key=index>e</p>))
    div>;
  

现在将显示 三个 内容为 1 到 3 的段落,因为这两个数组映射产生元素 带有键,但也带有键 碰撞。由于 b 中的所有元素都有与 a 中的元素发生冲突的键,因此 React 会将它们丢弃,现在你有一个 bug 需要寻找。

当然,如果您正在编写正确的 React,那么您通常不会遇到这种情况。总是使用键,并且总是确保它们是唯一的识别您正在映射的元素。

也就是说,我已经提交了https://github.com/reactjs/reactjs.org/issues/3732,希望能够更新该文本以明确说明该文本不是在谈论您的代码正在运行的任何迭代的索引,而是它自己的内部子节点索引。

【讨论】:

这个答案是错误的。无论是否指定了索引,元素的路径完全相同。 “但肯定不总是”是什么让你这么认为? 关键点是,当没有给出键时,react 会做什么。文档声明,它将采用数组中元素的索引(显然是最终的子数组,而不是一些将被连接的中间体)。您的示例可能是 OP 做错了什么,但不是辩论的内容(在人们声称文档错误之前,我实际上是在考虑否决这个问题,因为我认为它只是陈述了错误的东西,没有可重复的示例)。人们努力解释为什么经常需要密钥,虽然这很好,但它是 OT。 除了“显然最终的孩子数组”显然一点都不明显。可能值得有人提交文档问题,以便 React 人员可以明确说明,而不是让人们猜测它在谈论哪个索引。 好的,正确的,所以只有当你通过设置碰撞键来欺骗到 React 时才会出错。用一个简单的arr.map((el, i) =&gt; &lt;div key=i&gt;el&lt;/div&gt;)你cannot create that scenario 不,当然不是,如果你可以用一个 map() 创建这个场景,那么当 React 仍然是零点次要时,这个问题就会浮出水面。因此,从这个答案中引用我自己的话:“那些可能(并且经常会)与您的迭代索引相同,但肯定不总是”,因为 React 不使用迭代代码固有的索引,它使用虚拟 dom 中虚拟元素的子索引。与文本所暗示的不同,当您了解足够的英语来阅读文档时,但 React 还不够了解 React 人对这些词的含义。

以上是关于为啥提供 key=index 会导致与不定义键不同的行为? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

Magento:是啥导致重新索引...以及它为啥会中断?

引入 FOREIGN KEY 约束可能会导致循环或多个级联路径 - 为啥?

为啥这会导致无限请求循环?

为啥这会导致无限请求循环?

URL最后结尾反斜杠(/)加与不加区别

为啥我的 Jose4j JSON Web Key 会导致这个 InvalidKeyException?