理解在React.js中用于数组子元素的唯一键值

27 浏览
0 Comments

理解在React.js中用于数组子元素的唯一键值

我正在构建一个 React 组件,可以接受一个 JSON 数据源并创建一个可排序的表格。

每个动态数据行都有一个唯一的键,但我仍然收到以下错误:

数组中的每个子级都应该有一个唯一的“key”属性。

请检查 TableComponent 的呈现方法。

我的 TableComponent 呈现方法返回:

{ rows }

 

TableHeader 组件是一个单行,也有一个唯一的键。

rows 中的每个 row 都由具有唯一键的组件构建:


TableRowItem 如下所示:

var TableRowItem = React.createClass({
  render: function() {
    var td = function() {
        return this.props.columns.map(function(c) {
          return{this.props.data[c]}

; }, this); }.bind(this); return (

{ td(this.props.item) }

) } });

是什么导致了唯一键属性的错误?

admin 更改状态以发布 2023年5月22日
0
0 Comments

在迭代数组时要小心!

有一个常见的误解,即使用数组中元素的索引是一种可接受的方式,可以抑制您可能熟悉的错误:

Each child in an array should have a unique "key" prop.

但是,在许多情况下,这并不是这样的!这是一种反模式,可能在某些情况下导致不良行为。

理解key属性

React使用keyprop来理解组件与DOM元素的关系,然后用于协调过程。 因此,关键始终保持唯一非常重要,否则React有很大几率混淆元素并修改不正确的元素。同样重要的是,这些键在所有重新渲染过程中保持静态,以保持最佳性能。

话虽如此,只要知道数组完全是静态的,就不必总是应用上述方法。但是,尽可能应用最佳实践是受到鼓励的。

一个React开发人员在此GitHub问题中说:

  • 关键是不是真的关于性能,而是更多关于身份(从而导致更好的性能)。随机分配和更改值不是身份
  • 如果不知道如何对数据进行建模,我们实际上无法自动提供键[自动]。如果没有ID,我建议使用某种哈希函数
  • 当我们使用数组时,我们已经有了内部密钥,但它们是数组中的索引。插入新元素时,这些密钥就不对了。

简而言之,key应该是:

  • 唯一 - 一个键不能与兄弟组件的键相同。
  • 固定 - 一个键应该在重新渲染时保持不变。

使用 key 属性

根据上面的解释,仔细研究以下示例,并在可能的情况下尝试实现建议的方法。


不好(潜在的)


    {rows.map((row, i) => {
        return ;
    })}

当在React中迭代数组时,这可以说是最常见的错误。这个方法在技术上并没有“错误”,只是如果你不知道你在做什么,那就是“危险”的。如果你正在遍历一个静态数组,那么这是一个完全有效的方法(例如你的导航菜单中的链接数组)。然而,如果你正在添加,删除,重新排序或过滤项目,那么你就需要小心了。在官方文档中查看这个详细的解释

class MyApp extends React.Component {
  constructor() {
    super();
    this.state = {
      arr: ["Item 1"]
    }
  }
  click = () => {
    this.setState({
      arr: ['Item ' + (this.state.arr.length+1)].concat(this.state.arr),
    });
  }
  render() {
    return(
        
        
    {this.state.arr.map( (item, i) => {item + " "} )}
); } } const Item = (props) => { return (
  • ); } ReactDOM.render(, document.getElementById("app"));


    
    
    

    在这个代码片段中,我们正在使用一个非静态数组,并且我们不限制自己将其用作堆栈。这是一种不安全的方法(你会看到为什么)。请注意,当我们添加项目到数组的开头(基本上是unshift)时,每个的值保持不变。为什么?因为key没有唯一标识每个项。

    换句话说,一开始Item 1key={0}。当我们添加第二个项目时,顶部项目变成Item 2,其后是Item 1作为第二个项目。然而,现在Item 1key={1}而不是key={0}了。取而代之的是,Item 2现在有了key={0}

    因此,React 认为 元素没有变化,因为具有键为 0Item 一直在顶部!

    那么为什么这种方法只有有时是不好的?

    如果数组被过滤、重新排列或添加/删除项,则此方法仅在这种情况下才具有风险。如果它始终是静态的,那么使用它是完全安全的。例如,像 ["Home", "Products", "Contact us"] 这样的导航菜单可以使用此方法安全地迭代,因为您可能永远不会添加新链接或重新排列它们。

    简而言之,以下是可以 安全地 使用索引作为 key 的情况:

    • 数组是静态的,并且永远不会改变。
    • 数组从未被过滤(显示数组的子集)。
    • 数组从未被重新排序。
    • 数组用作堆栈或 LIFO(后进先出)。换句话说,只能在数组末尾添加(即推入),并且只能删除最后一项(即弹出)。

    如果我们在上面的片段中将添加的项目 推送 到数组末尾,每个现有项目的顺序将始终正确。


    非常糟糕

    
        {rows.map((row) => {
            return ;
        })}
    
    

    虽然这种方法可能保证键的唯一性,但它始终会强制 React 重新呈现列表中的每个项目,即使不需要。这是一个非常糟糕的解决方案,因为它会极大地影响性能。更不用说不能排除在 Math.random() 产生两次相同数字的情况下出现键冲突的可能性。

    不稳定的键(比如由Math.random()产生的键)将导致许多组件实例和DOM节点不必要地被重新创建,这可能会导致性能下降并且子组件状态丢失。


    非常好

    
        {rows.map((row) => {
            return ;
        })}
    
    

    这可能是最好的方法,因为它使用了一个在数据集中每个项目中都是唯一的属性。例如,如果rows包含从数据库获取的数据,则可以使用表的主键(通常是一个自增的数字)。

    选择键的最佳方法是使用一个字符串,该字符串在其兄弟姐妹中唯一标识列表项。通常,您会使用数据中的ID作为键。


    componentWillMount() {
      let rows = this.props.rows.map(item => { 
        return {uid: SomeLibrary.generateUniqueID(), value: item};
      });
    }
    ...
    
        {rows.map((row) => {
            return ;
        })}
    
    

    这也是一个好方法。如果您的数据集不包含任何保证唯一性的数据(例如一组任意数字的数组),则存在键冲突的可能性。在这种情况下,最好在迭代数据集之前为每个项手动生成一个唯一标识符。最好在组件挂载或接收数据集时(例如从props或异步API调用中),这样只需执行一次,而不是每次组件重新渲染时都去生成。已经有一些库可以提供这样的键。以下是一个示例:react-key-index

    0
    0 Comments

    你应该给每个子元素 以及子元素内的每个元素 添加一个键。

    这样React就可以处理最小的DOM变化。

    在你的代码中,每个都试图渲染一些没有键的子元素。

    请查看 这个例子

    尝试从 div 中的 元素中删除 key={i} 并查看控制台。

    在示例中,如果我们没有给 元素提供键,并且我们想要更新 它的 object.city,React需要重新渲染整个行而不仅仅是这个元素。

    这是代码:

    const data = [
      { name: "Nuri", age: 28, city: "HO" },
      { name: "Talib", age: 82, city: "HN" },
      { name: "Jenny", age: 41, city: "IT" },
    ];
    const ExampleComponent = React.createClass({
      render: function () {
        const infoData = this.props.info;
        return (
            {infoData.map((object, i) => {
              return (
                  {[
                    object.name,
                    // remove the key
                    
                      {" "}
                      {object.city}{" "}
                    ,
                    object.age,
                  ]}
              );
            })}
        );
      },
    });
    React.render(, document.body);
    


    底部由@Chris发布的答案比这个答案更详细。

    关于调和过程中键的重要性的React文档:

    0