首页 文章

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

提问于
浏览
10

我已经做了一年多的反应 . 我主要使用.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>
  )
}

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

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元素添加一个独特的键,除了我上面提到的,哪个是最有效和推荐的?

4 回答

  • -2

    您可以将 Date.now()index 一起使用,您的代码将为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>
      )
    }
    
  • 5

    首先, avoid using random keys .

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

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

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

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

    • Components of different type will create a new tree :这意味着,在将旧树与新树进行比较时,如果协调程序遇到节点确实更改了其类型(例如 <Button /><NotButton /> ),则会导致我们的Button也与其子节点一起卸载,并且NotButton也可以与它的孩子一起安装 .

    • We can hint React on how instances are preserved on VDOM, by avoiding recreating them. These hints are provided by us with keys. :在确定是否应保留节点中的实例(因为其类型保持不变)之后,协调程序将迭代该节点的子节点以进行比较 .

    现在说我们有这个:

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

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

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

    算法将如下:

    • 在两个VDOM中比较 <divs> . 由于它们具有相同的类型,因此我们不需要重新创建新树 . 道具是相同的,因此此时没有适用于DOM的更改 .

    • 按钮 OneZero 进行比较 . Reconciler检测到这是一个道具更改,然后使用此 Headers 更新DOM .

    • 按钮 TwoOne 进行比较 . Reconcilier还会在此处检测道具更改并使用DOM来编写此更改 .

    • 检测到新的 Button 被添加为最后一个子节点,因此在VDOM上创建一个新的 Button 实例并在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>
    

    算法将如下:

    • 在两个VDOM中比较 <divs> . 由于它们具有相同的类型,因此我们不需要重新创建新树 . 道具是相同的,因此此时没有适用于DOM的更改 .

    • 成为孩子的第一个孩子 . 'It' s a Button ', says the reconciler. '并且有一个键' ('一个'). Then, seeks for a children whose key is the same in the new children list. '哦,我遇到了它!但调和者意识到 there is no change on its props . 然后,这个不需要DOM操作 .

    • 第二个 Button 发生相同的情况,它将通过 keys 而不是 index 进行比较 . 意识到它是相同的实例并且没有更改任何道具,因此React决定不对DOM应用更改 .

    • 对于带有'Zero'键的 Button ,自 there no exists a child with the same key 以来,意识到应该在VDOM上创建一个实例,并且应该在DOM上写入此更改 .

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

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

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

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

    如果渲染输出是这样的

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

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

    'Hey! I' ve发现 Button 带有 F67BMkd== 键,但未在下一个中找到 . 我'll delete it.' 'Oh! I'已经遇到 Button 并带有 SHDSA++5 键!让's create a new one' .

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

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

    希望能帮助到你 .

  • -3

    md5 sha1甚至sha256的内容 .

  • 10

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

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

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

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

    另请注意:

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

    However real answer to your question lives here: https://medium.com/@robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318

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

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

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

    并迭代如下:

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

相关问题