简体   繁体   English

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

[英]Different ways to add a key to JSX element in loop in React

I have been working on react for more than an year now. 我已经做了一年多的反应。 I have mostly played with iterating an array using .map, .forEach, .filter or using Object.keys and Object.values if it is an object. 我主要使用.map,.forEach,.filter或使用Object.keys和Object.values迭代一个数组(如果它是一个对象)。

But what are the different ways to add a unique key to jsx element. 但是为jsx元素添加唯一键的不同方法有哪些。 Below is what I have been used to till now 以下是我到目前为止所习惯的

Using unique id from data as key to key prop: 使用数据中的唯一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>
  )
}

Using index as key to key prop: 使用索引作为关键道具的关键:

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

Is there any other ways to add a unique key to the jsx element apart from what I have mentioned above and which is most efficient and recommended? 除了我上面提到的以及最有效和推荐的方法之外,还有其他方法可以为jsx元素添加唯一键吗?

First of all, avoid using random keys . 首先, 避免使用随机密钥

There are a lot of ways to write keys, and some will perform better than others. 写密钥有很多种方法,有些方法比其他方式更好。

To understand how the keys we've chosen impacts on performance, it's necessary to understand React's Reconciliation Algorithm. 要了解我们选择的密钥如何影响性能,有必要了解React的协调算法。

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

tl;dr Introduces a heuristic for comparing Virtual DOM trees to make this comparison O(n), with n the nodes of this VDOM tree. tl; dr引入一种启发式,用于比较虚拟DOM树以进行此比较O(n),其中n为此VDOM树的节点。 This heuristic can be split in these points: 这种启发式可以分为以下两点:

  • Components of different type will create a new tree : This means that, while comparing the old tree with the new one, if the reconciler encounters that a node did change its type (eg <Button /> to <NotButton /> ), will cause to our Button to be unmounted with its children as well, and NotButton to be mounted with its children, as well. 不同类型的组件将创建一个新树 :这意味着,在将旧树与新树进行比较时,如果协调程序遇到节点确实更改其类型(例如<Button /><NotButton /> ),将导致将我们的Button与其子项一起卸载,并将NotButton与其子项一起安装。
  • We can hint React on how instances are preserved on VDOM, by avoiding recreating them. 我们可以提示React如何在VDOM上保留实例,避免重新创建它们。 These hints are provided by us with keys. 这些提示由我们提供密钥。 : After deciding if the instance in a node should be preserved (because its type remains the same), the reconciler will iterate on that node's children to compare them. :在确定是否应保留节点中的实例(因为其类型保持不变)之后,协调程序将迭代该节点的子节点以进行比较。

Supose now that we have this: 现在说我们有这个:

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

And we'd like to add a Button to the DOM on the next render, say 我们想在下一个渲染中为DOM添加一个Button,比如说

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

The algorithm will go as follows: 算法将如下:

  • Compares <divs> in both VDOMs. 比较两个VDOM中的<divs> Since these have the same type, we don't need to recreate the new tree. 由于它们具有相同的类型,因此我们不需要重新创建新树。 Props are the same so there are no changes to apply to the DOM at this point. 道具是相同的,因此此时没有适用于DOM的更改。
  • Button One compares against Zero . Button OneZero比较。 Reconciler detects that here was a props change, then updates the DOM with this title. Reconciler检测到这是一个道具更改,然后使用此标题更新DOM。
  • Button Two compares against One . Button TwoOne Reconcilier also detects a props change here and uses the DOM to write this change. Reconcilier还会在此处检测道具更改并使用DOM来编写此更改。
  • Detects that a new Button is added as last child, so creates a new Button instance at VDOM and write this change at DOM. 检测到新Button被添加为最后一个子节点,因此在VDOM处创建一个新的Button实例并在DOM处写入此更改。

Notice that these has many operations on the DOM, because it compared components by their index. 请注意,它们在DOM上有许多操作,因为它通过索引比较组件。

Now, we can fix this behavior by letting know to our reconciler that these instances should be reused. 现在,我们可以通过告知我们的协调程序应该重用这些实例来解决此问题。 Now, let's have this: 现在,让我们有这个:

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

And we'd like to add a Button to the DOM on the next render, say 我们想在下一个渲染中为DOM添加一个Button,比如说

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

The algorithm will go as follows: 算法将如下:

  • Compares <divs> in both VDOMs. 比较两个VDOM中的<divs> Since these have the same type, we don't need to recreate the new tree. 由于它们具有相同的类型,因此我们不需要重新创建新树。 Props are the same so there are no changes to apply to the DOM at this point. 道具是相同的,因此此时没有适用于DOM的更改。
  • Takes the first child of childrens. 成为孩子的第一个孩子。 'It's a Button ', says the reconciler. 调和者说,这是一个Button 'And has a key' ('One'). '并且有一把钥匙'('一')。 Then, seeks for a children whose key is the same in the new children list. 然后,在新的儿童名单中寻找关键相同的孩子。 'Oh, I encountered it!' “哦,我遇到了!” but the reconciler realizes that there is no change on its props . 但是,调和者意识到它的道具没有变化 Then, no DOM operations will be needed for this one. 然后,这个不需要DOM操作。
  • The same scenario occurs with the second Button , it will compare by keys instead of by index . 第二个Button会出现相同的情况,它将通过keys而不是index进行比较。 Realizes that it's the same instance and no props were changed, so React decides to not apply changes on the DOM. 意识到它是相同的实例并且没有更改任何道具,因此React决定不对DOM应用更改。
  • For the Button with 'Zero' key, since there no exists a child with the same key , realizes that an instance should be created at VDOM, and this change should be written on DOM. 对于具有“零”键的Button ,由于没有具有相同键的子项 ,因此意识到应该在VDOM上创建实例,并且此更改应该写在DOM上。

So, using keys by predictable contents helps the reconciler to perform less operations on the DOM. 因此,通过可预测内容使用密钥有助于协调程序对DOM执行较少的操作。 Healthy keys are those that can be inferred from the object that is being mapped, like a name , or an id or even an url if we are transforming urls to <imgs /> . 健康键是那些可以从被映射的对象推断出来的键,如nameid或甚至是url如果我们将urls转换为<imgs />

What about key=index ? 那么key=index呢? Will have no effect, since by default, reconciler compares by position, ie its index. 将无效,因为默认情况下,协调程序按位置进行比较,即其索引。

These keys should be globally unique? 这些键应该是全球唯一的吗? Not necessarily. 不必要。 These should be unique among siblings, so reconciler can distinguish them while iterating by a node's children. 这些在兄弟姐妹中应该是唯一的,因此协调者可以在节点的子节点迭代时区分它们。

What about random keys? 随机键怎么样? These should be avoided at all costs. 应不惜一切代价避免这些。 If a key changes on every render, this will be keeping React destroying and creating instances on the VDOM (and hence, making extra writes on the DOM) since a component with a key wasn't found among the new children, but a new one with the same type. 如果一个键在每个渲染上发生变化,这将使React破坏并在VDOM上创建实例(因此,在DOM上进行额外的写入),因为在新的子节点中找不到具有键的组件,而是新的子节点具有相同的类型。

If the render output is like 如果渲染输出是这样的

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

Then, each time render is executed (eg due a props/state change, or even if it's parent is being re-rendered and our shouldComponentUpdate returns true ), a new randomGenerator() key will be generated. 然后,每次执行render (例如,由于道具/状态更改,或者即使它的父级正在重新渲染并且我们的shouldComponentUpdate返回true ),也会生成一个新的randomGenerator()键。 This will go like: 这将是:

'Hey! '嘿! I've found a Button with a F67BMkd== key, but none was found in the next one. 我找到了一个带有F67BMkd==键的Button ,但没有找到下一个。 I'll delete it.' 我会删除它。 'Oh! '哦! I've encountered a Button with a SHDSA++5 key! 我遇到了一个带有SHDSA++5键的Button Let's create a new one'. 让我们创建一个新的'。

Whenever the reconciler tells that an instance should be deleted and unmounted, its internal state will be lost; 每当协调程序告知应删除和卸载实例时,其内部状态将丢失; even if we mount it again. 即使我们再次安装它。 The instance at VDOM will not be preserved in this case. 在这种情况下,VDOM的实例不会被保留。

The Button was the same, but the reconciler did a mess at DOM. Button是相同的,但协调员在DOM上弄得一团糟。

Hope it helps. 希望能帮助到你。

The best way to pick a key is to use a string that uniquely identifies a list item among its siblings. 选择密钥的最佳方法是使用在其兄弟姐妹中唯一标识列表项的字符串。 Most often you would use IDs from your data as keys: 大多数情况下,您会使用数据中的ID作为键:

const todoItems = todos.map((todo) =>
  <li key={todo.id}>
    {todo.text}
  </li>
);

When you don't have stable IDs for rendered items, you may use the item index as a key as a last resort: 当您没有渲染项目的稳定ID时,您可以使用项目索引作为关键作为最后的手段:

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

Also note: 另请注意:

Keys used within arrays should be unique among their siblings. 数组中使用的键在其兄弟姐妹中应该是唯一的。 However they don't need to be globally unique. 但是,它们不需要是全球唯一的。

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

There are many libraries that generate random unique ids such as shortid or uuid (which is the most popular one, just look at the number of downloads) or else just create your own function that generates random strings. 有许多库可以生成随机唯一ID,例如shortiduuid (这是最受欢迎的ID,只需查看下载次数),或者只是创建自己的函数来生成随机字符串。

You can add them directly into object in array 您可以将它们直接添加到数组中的对象中

const todos = [
  { 
    id: uuid(),
    text: 'foo',
  }
]

and iterate like so: 并迭代如下:

const todoItems = todos.map(({id, text}) =>
  <li key={id}>
    {text}
  </li>
);

md5 sha1甚至sha256的内容。

You can use Date.now() with index , your code will be as Ex. 您可以将Date.now()索引一起使用 ,您的代码将为Ex。

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

render(){
  const items = data.map((item, i) => {
    let keyValue = Date.now()+i;
    return <span key={keyValue}>{item.name}</span>;
  }
  return(
     <div>
        {items}
     </div>
  )
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM