当前位置: 首页 > 面试题库 >

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

仉宪
2023-03-14
问题内容

我已经从事反应超过一年了。我主要使用.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元素添加唯一键,并且最有效和推荐的方法是?


问题答案:

首先, 避免使用随机密钥

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

要了解我们选择的密钥如何影响性能,有必要了解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做了一个烂摊子。

希望能帮助到你。



 类似资料:
  • 问题内容: 我正在React中尝试执行以下操作(其中ObjectRow是一个单独的组件): 我意识到并理解为什么这是无效的,因为它映射到函数调用。但是,由于来自模板领域并且是的新手,所以我不确定如何实现上述目标(多次添加组件)。 问题答案: 就像您只是在调用JavaScript函数一样。您不能使用循环来调用函数的参数: 查看如何将函数作为参数传递给循环-导致语法错误。 但是您可以创建一个数组,然后

  • 为了更新一些证书,我必须将jks密钥库转换为PKCS#12密钥库,并在转换过程中包含私钥。我们收到的密钥库是JKS密钥库,但是我们的网络服务器使用PKCS#12密钥库,我们收到的密钥库只包含证书,而不包含私钥。 我试着用谷歌搜索并阅读SO的几个条目,我只想出了一个解决方案,这似乎更像是一个变通方法,而不是一个好的方法,所以我想知道是否有人有更好的方法如何注入私钥,并以更简单的方式从jks密钥库转换

  • 问题内容: 我正在React中尝试做类似以下的事情(其中ObjectRow是一个单独的组件): 我意识到并理解为什么这是无效的,因为它映射到函数调用。但是,由于来自模板领域并且是的新手,所以我不确定如何实现上述目标(多次添加组件)。 问题答案: 就像您只是在调用JavaScript函数一样。您不能使用循环来调用函数的参数: 查看如何将函数作为参数传递给循环,这当然是语法错误。 但是您可以创建一个数

  • 我有一个比较简单的问题,就是尝试将内联脚本添加到React组件中。我目前所掌握的: 我还尝试过: 这两种方法似乎都无法执行所需的脚本。我想我错过的是一件简单的事。有人能帮忙吗? PS:忽略foobar,我有一个真实的id实际上在使用,我不想分享。

  • 我对react很陌生(我只使用过abit类),我想把输入值添加到并在屏幕上写出来,但我的大脑被锁住了,我不知道如何...

  • 鉴于这一目标: 我想在JSX中循环使用它。这项工作: 但这并不是(社会未定义): 第二个示例中未定义社交的原因是什么?我假设内括号存在范围界定问题,但我尚未成功修复它。 我可以用对象键做一个,也可以像本文中那样做,但这与我的工作示例没有太大区别。 为了清楚起见,我在第二个示例中只想更清楚地说明作用域问题(或者语法错误,如果是的话)。