小编典典

在React循环中向JSX元素添加密钥的不同方法

reactjs

我已经从事反应超过一年了。我主要使用.map,.forEach,.filter或如果对象是Object.keys和Object.values来迭代数组。

但是向jsx元素添加唯一键的不同方法是什么。到目前为止,我已经习惯了以下内容

使用数据中的唯一ID作为键prop的键:

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>
  )
}

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

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>
  )
}

除了我上面提到的以外,还有没有其他方法可以向jsx元素添加唯一键,并且最有效和推荐的方法是?


阅读 310

收藏
2020-07-22

共1个答案

小编典典

首先, 避免使用随机密钥

有很多写键的方法,有些会比其他的性能更好。

要了解我们选择的密钥如何影响性能,有必要了解React的对帐算法。

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

tl; dr引入了一种启发式方法,用于比较虚拟DOM树与该VDOM树的n个节点进行比较O(n)。这种启发式方法可以分为以下几点:

  • 不同类型的组件将创建一个新的树 :这意味着,在与新的比较老的树,如果调解遇到一个节点并改变其类型(例如<Button /><NotButton />),会导致我们的按钮将要卸载其子项以及NotButton也将与其子项一起安装。
  • 通过避免重新创建实例,我们可以向React暗示如何在VDOM上保留实例。 这些提示是由我们提供的键。:在决定是否应保留节点中的实例(因为其类型保持不变)之后,协调程序将迭代该节点的子节点以对其进行比较。

现在假设我们有这个:

<div>
  <Button title="One" />
  <Button title="Two" />
</div>

我们想在下一个渲染中向DOM添加一个Button,例如

<div>
  <Button title="Zero" />
  <Button title="One" />
  <Button title="Two" />
</div>

该算法将如下所示:

  • <divs>在两个VDOM中进行比较。由于它们具有相同的类型,因此我们无需重新创建新树。道具是相同的,因此此时没有更改可应用于DOM。
  • 按钮One与进行比较Zero。协调器检测到这里是道具更改,然后使用此标题更新DOM。
  • 按钮Two与进行比较One。协调器还会在此处检测道具更改,并使用DOM编写此更改。
  • 检测到新Button添加的对象是最后一个孩子,因此Button在VDOM 创建一个新实例,并将此更改写入DOM。

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

现在,我们可以通过告知协调器这些实例应被重用来解决此问题。现在,让我们有这个:

<div>
  <Button title="One" key="One" />
  <Button title="Two" key="Two" />
</div>

我们想在下一个渲染中向DOM添加一个Button,例如

<div>
  <Button title="Zero" key="Zero" />
  <Button title="One" key="One" />
  <Button title="Two" key"Two" />
</div>

该算法将如下所示:

  • <divs>在两个VDOM中进行比较。由于它们具有相同的类型,因此我们无需重新创建新树。道具是相同的,因此此时没有更改可应用于DOM。
  • 带孩子的第一个孩子。Button调解员说,这是一个。“并且有钥匙”(“一个”)。然后,在新的子代列表中寻找密钥相同的子代。“哦,我遇到了!” 但是调解员意识到 道具没有改变 。这样一来,将不需要DOM操作。
  • 第二种情况相同Button,它将用keys而不是进行比较index。意识到它是同一个实例,并且没有更改道具,因此React决定不在DOM上应用更改。
  • 对于Button具有“零”键的键,由于 不存在具有相同键的子键 ,因此意识到应该在VDOM上创建实例,并且此更改应写在DOM上。

因此,通过可预测的内容使用密钥有助于协调器在DOM上执行较少的操作。健康键是可以从正在映射的对象中推断出来的键,例如name,如果要转换为idurl则为,甚至urls<imgs />

key=index呢 将不起作用,因为默认情况下,协调程序会按位置(即其索引)进行比较。

这些键应该是全局唯一的吗?不必要。这些在兄弟姐妹之间应该是唯一的,因此协调程序可以在节点的子节点进行迭代时区分它们。

随机密钥呢?应不惜一切代价避免这些情况。如果每个渲染上都有一个键更改,这将使React破坏并在VDOM上创建实例(并因此在DOM上进行额外的写操作),因为在新的子级中没有找到带有键的组件,而是一个新的具有相同的类型。

如果渲染输出像

<div>
  <Button key={randomGenerator()} />
</div>

然后,每次render执行(例如,由于道具/状态更改,或者即使其父级被重新渲染并shouldComponentUpdate返回true),randomGenerator()也会生成一个新密钥。这将像:

‘嘿!我找到了一个ButtonF67BMkd==钥匙的钥匙,但下一个没有找到。我将其删除。’哦!我遇到了Button一把SHDSA++5钥匙!让我们创建一个新的。

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

Button是一样的,但在调解DOM做了一个烂摊子。

希望能帮助到你。

2020-07-22