javascript - 在React循环中向JSX元素添加 key 的不同方法

标签 javascript arrays reactjs loops key

我从事响应工作已经一年多了。我主要是使用.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>
  )
}

使用索引作为关键 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>
  )
}

除了我上面提到的方法之外,还有没有其他方法可以向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>
    

    该算法将如下所示:
  • 比较两个VDOM中的<divs>。由于它们具有相同的类型,因此我们无需重新创建新树。 Prop 是相同的,因此此时没有更改可应用于DOM。
  • 按钮OneZero进行比较。协调者检测到这里是 Prop 变更,然后使用此标题更新DOM。
  • 按钮TwoOne进行比较。协调器还会在此处检测 Prop 更改,并使用DOM编写此更改。
  • 检测到新的Button被添加为最后一个 child ,因此在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>。由于它们具有相同的类型,因此我们无需重新创建新树。 Prop 是相同的,因此此时没有更改可应用于DOM。
  • 带 child 的第一个 child 。调解员说,这是Button。 “并且有 key ”(“一个”)。然后,在新的子代列表中寻找密钥相同的子代。 “哦,我遇到了!”但协调器意识到的 Prop 没有变化。这样,就不需要DOM操作了。
  • 第二个Button也会发生相同的情况,它将通过keys而不是index进行比较。意识到它是同一个实例,并且没有更改 Prop ,因此React决定不在DOM上应用更改。
  • 对于具有“零”键的Button,由于不存在具有相同键的子级,因此意识到应该在VDOM上创建实例,并且此更改应写在DOM上。

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

    key=index呢?将没有任何作用,因为默认情况下,协调器会按位置(即其索引)进行比较。

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

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

    如果渲染输出像
    <div>
      <Button key={randomGenerator()} />
    </div>
    

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

    '嘿!我找到了带有Button键的F67BMkd==,但是在下一个中没有找到。我将其删除。
    '哦!我遇到了带有Button键的SHDSA++5!让我们创建一个新的。

    每当协调程序告知应删除和卸载实例时,其内部状态都会丢失;即使我们再次安装它。
    在这种情况下,将不会保留VDOM上的实例。
    Button是相同的,但是协调器在DOM上一团糟。

    希望能帮助到你。

    关于javascript - 在React循环中向JSX元素添加 key 的不同方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52196127/

    相关文章:

    reactjs - (通用React + redux + React-router)如何避免在初始浏览器加载时重新获取路由数据?

    javascript - 尝试输入换行符会创建一个逗号

    javascript - 在服务器端使用带有 Django REST 的 AngularJS 实现表单验证插件

    arrays - 以错误的顺序在 block 内循环附加到数组 - Swift 2.0

    c++ - 如何对 Eigen 中的二维数组的列执行简单的算术运算

    reactjs - 在 redux 中,为什么我们必须在 reducer 中保持状态不可变?

    javascript - 如何在 native react 中有条件地渲染警报组件

    JavaScript 重定向不起作用

    javascript - 如何在不使用正则表达式的情况下检查输入是否为表情符号?

    C++ 如何创建 std::list 数组?