了解 React.js 中数组子项的唯一键

Understanding unique keys for array children in React.js

我正在构建一个接受 JSON 数据源并创建 sortable table.
的 React 组件 每个动态数据行都有一个分配给它的唯一键,但我仍然收到以下错误:

Each child in an array should have a unique "key" prop.
Check the render method of TableComponent.

我的 TableComponent 渲染方法 returns:

<table>
  <thead key="thead">
    <TableHeader columns={columnNames}/>
  </thead>
  <tbody key="tbody">
    { rows }
  </tbody>
</table>

TableHeader 组件是单行,并且还分配了一个唯一键。

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

<TableRowItem key={item.id} data={item} columns={columnNames}/>

TableRowItem 看起来像这样:

var TableRowItem = React.createClass({
  render: function() {

    var td = function() {
        return this.props.columns.map(function(c) {
          return <td key={this.props.data[c]}>{this.props.data[c]}</td>;
        }, this);
      }.bind(this);

    return (
      <tr>{ td(this.props.item) }</tr>
    )
  }
});

是什么导致唯一键道具错误?

您应该为每个子项 以及子项中的每个元素添加一个键

这种方式 React 可以处理最小的 DOM 变化。

在您的代码中,每个 <TableRowItem key={item.id} data={item} columns={columnNames}/> 都试图在没有密钥的情况下渲染其中的一些子项。

检查this example

尝试从 div 中的 <b></b> 元素中删除 key={i}(并检查控制台)。

在示例中,如果我们不给 <b> 元素一个键,而我们只想更新 object.city,React 需要重新渲染整行而不是元素。

代码如下:

var data = [{name:'Jhon', age:28, city:'HO'},
            {name:'Onhj', age:82, city:'HN'},
            {name:'Nohj', age:41, city:'IT'}
           ];

var Hello = React.createClass({
    
    render: function() {
            
      var _data = this.props.info;
      console.log(_data);
      return(
        <div>
            {_data.map(function(object, i){
               return <div className={"row"} key={i}> 
                          {[ object.name ,
                             // remove the key
                             <b className="fosfo" key={i}> {object.city} </b> , 
                             object.age
                          ]}
                      </div>; 
             })}
        </div>
       );
    }
});
 
React.render(<Hello info={data} />, document.body);

比这个答案更详细。

关于键在协调中的重要性的 React 文档:Keys

迭代数组时要小心!!

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

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

然而,在很多情况下并非如此!这是反模式,在某些情况下会导致不需要的行为


了解 key 道具

React 使用 key 属性来理解组件到 DOM 元素的关系,然后用于 reconciliation process。因此,关键 always 保持 unique 非常重要,否则 React 很可能会混淆元素并改变不正确的元素。同样重要的是,这些键 在所有重新渲染过程中保持静态 以保持最佳性能。

也就是说,总是 不需要应用上述内容,前提是已知数组是完全静态的。但是,我们鼓励尽可能采用最佳做法。

一位 React 开发人员在 this GitHub issue 中说:

  • key is not really about performance, it's more about identity (which in turn leads to better performance). randomly assigned and changing values are not identity
  • We can't realistically provide keys [automatically] without knowing how your data is modeled. I would suggest maybe using some sort of hashing function if you don't have ids
  • We already have internal keys when we use arrays, but they are the index in the array. When you insert a new element, those keys are wrong.

简而言之,一个key应该是:

  • Unique - 密钥不能与 sibling component.
  • 的密钥相同
  • 静态 - 密钥不应该在渲染之间改变。

使用 key 道具

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


不好(可能)

<tbody>
    {rows.map((row, i) => {
        return <ObjectRow key={i} />;
    })}
</tbody>

这可以说是在 React 中遍历数组时最常见的错误。这种方法在技术上并不 “错误”,它只是...... “危险” 如果你不知道自己在做什么。如果您正在遍历静态数组,那么这是一种完全有效的方法(例如,导航菜单中的链接数组)。但是,如果您要添加、删除、重新排序或过滤项目,则需要小心。看看官方文档中的这个detailed explanation

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(
      <div>
        <button onClick={this.click}>Add</button>
        <ul>
          {this.state.arr.map(
            (item, i) => <Item key={i} text={"Item " + i}>{item + " "}</Item>
          )}
        </ul>
      </div>
    );
  }
}

const Item = (props) => {
  return (
    <li>
      <label>{props.children}</label>
      <input value={props.text} />
    </li>
  );
}

ReactDOM.render(<MyApp />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

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

也就是说,最初Item 1key={0}。当我们添加第二个项目时,最上面的项目变成 Item 2,后面跟着 Item 1 作为第二个项目。但是,现在 Item 1key={1} 而不是 key={0}。相反,Item 2 现在有 key={0}!!

因此,React 认为 <input> 元素没有改变,因为带键 0Item 总是在顶部!

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

只有当数组以某种方式被过滤、重新排列或项目 added/removed 时,这种方法才会有风险。如果它始终是静态的,那么使用它是绝对安全的。例如,像 ["Home", "Products", "Contact us"] 这样的导航菜单可以使用此方法安全地迭代,因为您可能永远不会添加新链接或重新排列它们。

简而言之,此时您可以安全地使用索引 key:

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

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


很差

<tbody>
    {rows.map((row) => {
        return <ObjectRow key={Math.random()} />;
    })}
</tbody>

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

Unstable keys (like those produced by Math.random()) will cause many component instances and DOM nodes to be unnecessarily recreated, which can cause performance degradation and lost state in child components.


很好

<tbody>
    {rows.map((row) => {
        return <ObjectRow key={row.uniqueId} />;
    })}
</tbody>

这可以说是 best approach,因为它使用的 属性 对于数据集中的每个项目都是唯一的。例如,如果 rows 包含从数据库中获取的数据,则可以使用 table 的主键( 通常是一个自动递增的数字 )。

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


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

...

<tbody>
    {rows.map((row) => {
        return <ObjectRow key={row.uid} />;
    })}
</tbody>

这也是个好办法。如果您的数据集不包含任何保证唯一性的数据(例如任意数字的数组),则有可能发生键冲突。在这种情况下,最好在迭代之前为数据集中的每个项目手动生成一个唯一标识符。最好在安装组件或接收数据集时(例如来自props或来自异步API调用),以便仅执行此操作一次,而不是每次组件重新渲染。已经有少数图书馆可以为您提供此类密钥。这是一个示例:react-key-index.

警告:数组或迭代器中的每个子项都应该有一个唯一的 "key" 属性。

这是一个警告,因为我们要迭代的数组项需要唯一的相似性。

React 将迭代组件渲染处理为数组。

解决此问题的更好方法是为要迭代的数组项提供索引 over.for 示例:

class UsersState extends Component
    {
        state = {
            users: [
                {name:"shashank", age:20},
                {name:"vardan", age:30},
                {name:"somya", age:40}
            ]
        }
    render()
        {
            return(
                    <div>
                        {
                            this.state.users.map((user, index)=>{
                                return <UserState key={index} age={user.age}>{user.name}</UserState>
                            })
                        }
                    </div>
                )
        }

index 是 React 内置的 props。

只需将 唯一键 添加到您的组件

data.map((marker)=>{
    return(
        <YourComponents 
            key={data.id}     // <----- unique key
        />
    );
})

这是一个警告,但是 解决这个问题将使 Reacts 渲染更快,

这是因为React需要唯一标识列表中的每个项目。假设如果列表中某个元素的状态在 Reacts Virtual DOM 中发生变化,那么 React 需要弄清楚哪个元素发生了变化,以及在 DOM 中它需要更改的位置,以便浏览器 DOM将与 Reacts Virtual DOM.

同步

作为解决方案,只需为每个 li 标签引入一个 key 属性。此 key 应该是每个元素的唯一值。

检查:key = undef !!!

您还收到警告消息:

Each child in a list should have a unique "key" prop.

如果你的代码是正确的,但是如果

<ObjectRow key={someValue} />

someValue 未定义!!!请先检查一下。您可以节省时间。

var TableRowItem = React.createClass({
  render: function() {

    var td = function() {
        return this.props.columns.map(function(c, i) {
          return <td key={i}>{this.props.data[c]}</td>;
        }, this);
      }.bind(this);

    return (
      <tr>{ td(this.props.item) }</tr>
    )
  }
});

这将解决问题。

在反应中定义唯一键的最佳解决方案: 在地图中,您初始化名称 post 然后通过 key={post.id} 定义键,或者在我的代码中您看到我定义名称项然后通过 key={item.id} 定义键:

<div className="container">
                {posts.map(item =>(

                    <div className="card border-primary mb-3" key={item.id}>
                        <div className="card-header">{item.name}</div>
                    <div className="card-body" >
                <h4 className="card-title">{item.username}</h4>
                <p className="card-text">{item.email}</p>
                    </div>
                  </div>
                ))}
            </div>

这可能对某些人有帮助,也可能没有帮助,但它可能是一个快速参考。这也类似于上面给出的所有答案。

我有很多位置使用以下结构生成列表:

return (
    {myList.map(item => (
       <>
          <div class="some class"> 
             {item.someProperty} 
              ....
          </div>
       </>
     )}
 )
         

经过一些尝试和错误(以及一些挫折)后,向最外层的块添加一个键 属性 解决了它。另外,请注意 <> 标签现在已替换为 <div> 标签。

return (
  
    {myList.map((item, index) => (
       <div key={index}>
          <div class="some class"> 
             {item.someProperty} 
              ....
          </div>
       </div>
     )}
 )

当然,在上面的例子中,我一直天真地使用迭代索引(index)来填充键值。理想情况下,您会使用列表项独有的内容。

我 运行 进入此错误消息是因为 <></> 为数组中的某些项目返回,而 null 需要返回。

在 ReactJS 中,如果你正在渲染一个元素数组,你应该为每个元素都有一个唯一的键。通常那些有点情况正在创建一个列表。

示例:

function List() {
  const numbers = [0,1,2,3];
 
  return (
    <ul>{numbers.map((n) => <li> {n} </li>)}</ul>
  );
}

 ReactDOM.render(
  <List />,
  document.getElementById('root')
);

在上面的示例中,它使用 li 标签创建了一个动态列表,因此由于 li 标签没有唯一键,因此显示错误。

修复后:

function List() {
  const numbers = [0,1,2,3];
 
  return (
    <ul>{numbers.map((n) => <li key={n}> {n} </li>)}</ul>
  );
}

 ReactDOM.render(
  <List />,
  document.getElementById('root')
);

没有唯一键时使用地图的替代解决方案(反应不推荐eslint):

function List() {
  const numbers = [0,1,2,3,4,4];
 
  return (
    <ul>{numbers.map((n,i) => <li key={i}> {n} </li>)}</ul>
  );
}

 ReactDOM.render(
  <List />,
  document.getElementById('root')
);

实例:https://codepen.io/spmsupun/pen/wvWdGwG

我有一个唯一的密钥,只需要像这样将它作为道具传递:

<CompName key={msg._id} message={msg} />

此页面很有帮助:

https://reactjs.org/docs/lists-and-keys.html#keys

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

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

请参考List and Keys - React

在我的例子中,将 id 设置为标签

<tbody key={i}>

问题已解决

If you are getting error like :

> index.js:1 Warning: Each child in a list should have a unique "key" prop.

Check the render method of `Home`. See https://reactjs.org/link/warning-keys for more information.

Then Use inside map function like:

  {classes.map((user, index) => (
              <Card  **key={user.id}**></Card>
  ))}`enter code here`

您应该为 tbody 的每个子键使用唯一值,其中

  • 值不能与其同级相同(相同)
  • 渲染之间不应更改

例如键值可以是数据库id或者UUID (Universal Unique Identifier).

这里的键是手动处理的:

<tbody>
  {rows.map((row) => <ObjectRow key={row.uuid} />)}
</tbody>

你也可以让 React 使用 React.Children.toArray

来处理键
<tbody>
  {React.Children.toArray(rows.map((row) => <ObjectRow />))}
</tbody>

我遇到了类似的问题,但不准确。尝试了所有可能的解决方案,但无法消除该错误

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

然后我尝试在不同的本地主机上打开它。我不知道怎么做,但它奏效了!

这是一个简单的例子,我首先使用了 && 的反应条件,然后映射,在其中我添加了 key 用户 ID 以确保它是唯一的

 <tbody>
                                    {users &&
                                    users.map((user) => {
                                        return <tr key={user._id}>
                                            <td>{user.username}</td>
                                            <td><input
                                                name="isGoing"
                                                type="checkbox"
                                                checked={user.isEnabled}
                                                onChange={handleInputChangeNew}/></td>
                                            <td>{user.role.roleTitle} - {user.role.department.departmentName}</td>
                                            {/*<td className="text-right">
                                                    <Button>
                                                        ACTION
                                                    </Button>
                                                </td>*/}
                                        </tr>
                                    })
                                    }


                                    </tbody>

我不接受详细解释,但这个答案的关键是“关键” 只需将 key 属性放在您的标签中,并确保每次迭代时都赋予它唯一的价值

#确保键的值不与其他值冲突

例子

<div>
        {conversation.map(item => (
          <div key={item.id  } id={item.id}>
          </div>
        ))}
      </div>

其中 conversation 是一个类似于下面的数组:

  const conversation = [{id:"unique"+0,label:"OPEN"},{id:"unique"+1,label:"RESOLVED"},{id:"unique"+2,label:"ARCHIVED"},
   ]

If you are struggling with this error Each child in a list should have a unique "key" prop.

Solve by declaring index value to the key attribute inside the rendering element.

App.js component

import Map1 from './Map1';

const arr = [1,2,3,4,5];

const App = () => {
  return (
    <>
     
     <Map1 numb={arr} />     

    </>
  )
}

export default App

Map.js component

const Map1 = (props) => {

    let itemTwo = props.numb;
    let itemlist = itemTwo.map((item,index) => <li key={index}>{item}</li>)

    return (
        <>        
        <ul>
            <li style={liStyle}>{itemlist}</li>
        </ul>
        </>
    )
}

export default Map1

你的密钥应该是 unique.like 唯一的 id.And 你的代码应该是这样的

<div>
 {products.map(product => (
   <Product key={product.id}>
    </Product>
    ))}
  </div>
if we have array object data . then we are map for showing  the data . and pass the unique id (key = {product.id} ) because browser can selected the unique data 

example : [
    {
        "id": "1",
        "name": "walton glass door",
        "suplier": "walton group",
        "price": "50000",
        "quantity": "25",
        "description":"Walton Refrigerator is the Best Refrigerator brand in bv 
         Bangladesh "
    },
    {
        
        "id": "2",
        "name": "walton glass door",
        "suplier": "walton group",
        "price": "40000",
        "quantity": "5",
        "description":"Walton Refrigerator is the Best Refrigerator brand in 
         Bangladesh "
    },
}


now we are maping the data and pass the unique id: 
{
    products.map(product => <product product={product} key={product.id} 
    </product>)
}

这里有 React 文档,很好地解释了如何使用 Key 属性,key 应该在父组件中定义,不应在子组件内部使用。React Docs