在React中为循环中的JSX元素添加键的不同方法

Hem*_*ari 10 javascript arrays loops key reactjs

我已经做了一年多的反应.我主要使用.map,.forEach,.filter或使用Object.keys和Object.values迭代一个数组(如果它是一个对象).

但是为jsx元素添加唯一键的不同方法有哪些.以下是我到目前为止所习惯的

使用数据中的唯一ID作为关键道具的关键:

const data= [{"id": "01", "name": "abc"}, {"id": "02", "name": "xyz"}];

render(){
  const items = data.map(item => {
    return <span key={item.id}>{item.name}</span>;
  }
  return(
     <div>
        {items}
     </div>
  )
}
Run Code Online (Sandbox Code Playgroud)

使用索引作为关键道具的关键:

const data= [{"id": "01", "name": "abc"}, {"id": "02", "name": "xyz"}];

render(){
  const items = data.map((item, i) => {
    let keyValue = i+1;
    return <span key={keyValue}>{item.name}</span>;
  }
  return(
     <div>
        {items}
     </div>
  )
}
Run Code Online (Sandbox Code Playgroud)

除了我上面提到的以及最有效和推荐的方法之外,还有其他方法可以为jsx元素添加唯一键吗?

Man*_*tan 12

首先,避免使用随机密钥.

写密钥有很多种方法,有些方法比其他方式更好.

要了解我们选择的密钥如何影响性能,有必要了解React的协调算法.

https://reactjs.org/docs/reconciliation.html

tl; dr引入一种启发式,用于比较虚拟DOM树以进行此比较O(n),其中n为此VDOM树的节点.这种启发式可以分为以下两点:

  • 不同类型的组件将创建一个新树:这意味着,在将旧树与新树进行比较时,如果协调程序遇到节点确实更改其类型(例如<Button />,更改<NotButton />),将导致我们的Button被卸载孩子们也可以和孩子一起安装NotButton.
  • 我们可以提示React如何在VDOM上保留实例,避免重新创建它们.这些提示由我们提供密钥.:在确定是否应保留节点中的实例(因为其类型保持不变)之后,协调程序将迭代该节点的子节点以进行比较.

现在说我们有这个:

<div>
  <Button title="One" />
  <Button title="Two" />
</div>
Run Code Online (Sandbox Code Playgroud)

我们想在下一个渲染中为DOM添加一个Button,比如说

<div>
  <Button title="Zero" />
  <Button title="One" />
  <Button title="Two" />
</div>
Run Code Online (Sandbox Code Playgroud)

算法将如下:

  • 比较<divs>两个VDOM.由于它们具有相同的类型,因此我们不需要重新创建新树.道具是相同的,因此此时没有适用于DOM的更改.
  • 按钮One比较Zero.Reconciler检测到这是一个道具更改,然后使用此标题更新DOM.
  • 按钮Two比较One.Reconcilier还会在此处检测道具更改并使用DOM来编写此更改.
  • 检测到新Button的添加为最后一个子Button节点,因此在VDOM上创建一个新实例并在DOM处写入此更改.

请注意,它们在DOM上有许多操作,因为它通过索引比较组件.

现在,我们可以通过告知我们的协调程序应该重用这些实例来解决此问题.现在,让我们有这个:

<div>
  <Button title="One" key="One" />
  <Button title="Two" key="Two" />
</div>
Run Code Online (Sandbox Code Playgroud)

我们想在下一个渲染中为DOM添加一个Button,比如说

<div>
  <Button title="Zero" key="Zero" />
  <Button title="One" key="One" />
  <Button title="Two" key"Two" />
</div>
Run Code Online (Sandbox Code Playgroud)

算法将如下:

  • 比较<divs>两个VDOM.由于它们具有相同的类型,因此我们不需要重新创建新树.道具是相同的,因此此时没有适用于DOM的更改.
  • 成为孩子的第一个孩子."这是一个Button',"调和者说.'并且有一把钥匙'('一').然后,在新的儿童名单中寻找关键相同的孩子."哦,我遇到了!" 但是,调和者意识到它的道具没有变化.然后,这个不需要DOM操作.
  • 第二种情况发生相同的情况Button,它将通过keys而不是通过比较index.意识到它是相同的实例并且没有更改任何道具,因此React决定不对DOM应用更改.
  • 对于Button使用'Zero'键,因为没有相同键的子项,所以应该在VDOM上创建一个实例,并且应该在DOM上编写此更改.

因此,通过可预测内容使用密钥有助于协调程序对DOM执行较少的操作.健康键是可以从正被映射的对象推断出来的那些,如a name,id或者甚至是url我们正在转换urls的对象<imgs />.

怎么样key=index?将无效,因为默认情况下,协调程序按位置进行比较,即其索引.

这些键应该是全球唯一的吗?不必要.这些在兄弟姐妹中应该是唯一的,因此协调者可以在节点的子节点迭代时区分它们.

随机键怎么样?应不惜一切代价避免这些.如果一个键在每个渲染上发生变化,这将使React破坏并在VDOM上创建实例(因此,在DOM上进行额外的写入),因为在新的子节点中找不到具有键的组件,而是新的子节点具有相同的类型.

如果渲染输出是这样的

<div>
  <Button key={randomGenerator()} />
</div>
Run Code Online (Sandbox Code Playgroud)

然后,每次render执行(例如,由于道具/状态改变,或者即使它的父级正被重新渲染并且我们的shouldComponentUpdate返回true),randomGenerator()将生成新密钥.这将是:

'嘿!我找到了Button一把F67BMkd==钥匙,但没有找到钥匙.我会删除它.'哦!我遇到了Button一把SHDSA++5钥匙!让我们创建一个新的'.

每当协调程序告知应删除和卸载实例时,其内部状态将丢失; 即使我们再次安装它.在这种情况下,VDOM的实例不会被保留.

Button是相同的,但调和者在DOM上弄得一团糟.

希望能帮助到你.

  • 谢谢你的好解释. (2认同)

Kun*_*nok 5

选择密钥的最佳方法是使用在其兄弟姐妹中唯一标识列表项的字符串.大多数情况下,您会使用数据中的ID作为键:

const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
);
Run Code Online (Sandbox Code Playgroud)

当您没有渲染项目的稳定ID时,您可以使用项目索引作为关键作为最后的手段:

const todoItems = todos.map((todo, index) =>
  // Only do this if items have no stable IDs
  <li key={index}>
    {todo.text}
  </li>
);
Run Code Online (Sandbox Code Playgroud)

另请注意:

数组中使用的键在其兄弟姐妹中应该是唯一的.但是,它们不需要是全球唯一的.

但是你的问题的真实答案就在这里: https ://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318

有许多库可以生成随机唯一ID,例如shortiduuid(这是最受欢迎的ID ,只需查看下载次数),或者只是创建自己的函数来生成随机字符串.

您可以将它们直接添加到数组中的对象中

const todos = [
  { 
    id: uuid(),
    text: 'foo',
  }
]
Run Code Online (Sandbox Code Playgroud)

并迭代如下:

const todoItems = todos.map(({id, text}) =>
  <li key={id}>
    {text}
  </li>
);
Run Code Online (Sandbox Code Playgroud)