ReactJS:在数组元素中提供键的最佳方法是什么?

18

我刚开始学习ReactJS,尝试了其他类似问题的解决方案,但迄今为止没有成功。

这是我的工作代码:

import React from 'react';
import ReactDOM from 'react-dom';


const Numbers = ['2', '4', '6', '8'];

const NumbersList = (props) => (
   <ul>
      {
          props.Numbers.map (
             number => <li key={number}>{number * 2}</li>
          )
      }
    </ul>
)
ReactDOM.render(<NumbersList Numbers = {Numbers} />, document.getElementById('root') )

但是当我将数字数组传递为:

const Numbers = ['4', '4', '6', '8']

我遇到了这个错误:

警告:遇到两个具有相同 key 的子元素,4。key 应该是唯一的,以便组件在更新时保持其身份。

我的问题是:在这种情况下,怎样才能最好地给出 key?如果我使用数字(如上面的示例)作为 key,那么避免此警告的最佳解决方案是什么?

谢谢!

8个回答

20

当你没有明确定义的唯一属性来区分项目(一组非唯一原始数据),你可以使用索引。

注意:如果项目有一个唯一的id(而且非原始数据列表应该有),不要使用索引,因为这是一个反模式

const Numbers = ['2', '4', '4', '8'];

const NumbersList = (props) => (
  <ul>
  {
  props.Numbers.map (
    (number, index) => <li key={index}>{number * 2}</li>
  )}
  </ul>
)

ReactDOM.render(
  <NumbersList Numbers = {Numbers} />,
  document.getElementById('root')
)
<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="root"></div>


7
根据官方文档,我们被建议不要使用索引,那么作为初学者,除了保持好的习惯外,有没有其他方法来实现目标? - Ankit Pandey
1
看一下我添加的注释。通常情况下,您会呈现一个对象列表,每个对象都应该有一个唯一的属性作为键。但在这种情况下,由于值不是唯一的,您没有其他选择。 - Ori Drori
4
那么,如果我们没有唯一的值,我唯一的解决方案就是将索引作为键?那如果我们有动态值,并且它们不是唯一的呢?对于我的初学者水平问题请谅解。 - Ankit Pandey
它似乎不起作用。你能分享一下 jsfiddle 吗?谢谢! - Ankit Pandey
1
不是最好的建议。即使没有唯一的项目,您仍然不必仅使用索引,因为当基础数据更改时,VDOM将无法捕获更改。请参阅 https://dev59.com/31UL5IYBdhLWcg3wv6Vx#49841642在这种情况下,应该使用组合键。`const data = [1, 1, 2, 3, 4];<ul> {data.map((value, index) => ( <li key={${index}-${value}}>${value}</li> )) </ul>` - Bohdan Shulha
显示剩余3条评论

3
原因是每个<li>标签应该有一个唯一的键。你在<li>标签中分配了key=4,因为数组中的元素4是重复的。
编辑后:
我的错,我没有完全阅读问题。使用uuid包生成新ID:https://www.npmjs.com/package/uuid 它们也有文档说明。

尊敬的先生,恕我直言,您正在解释我的问题 :P - Ankit Pandey
@AnkitPandey 我已经编辑了我的答案。请尝试一下,如果有效的话,请告诉我。 - DadyByte
它起作用了。我在寻找一个不需要包的解决方案。感谢分享这个解决方案 :) - Ankit Pandey
使用 uuid 实际上并不容易。因为使用 key 的意思是在渲染之间不重复。然而,uuid 在每次渲染时生成不同的值。这使得不一致的 key 使用就像使用 index 一样。 - Halil Kayer

1

做这个操作时,同时添加一个唯一键和自定义数据。

import React from 'react';
import ReactDOM from 'react-dom';


const Numbers = ['2', '4', '6', '8'];

const NumbersList = (props) => (
   <ul>
      {
          props.Numbers.map (
             (number, i) => <li key={i} custom={number}>{number * 2}</li>
          )
      }
    </ul>
)
ReactDOM.render(<NumbersList Numbers = {Numbers} />, document.getElementById('root') )

关键属性应该是唯一的,因此我从map方法返回的值作为自定义属性,将是您想要为所有元素设置的数组数据。

谢谢!虽然添加了唯一键后代码可以正常工作,但没有自定义数据也是如此。 - Ankit Pandey

1
恭喜您,这个问题并没有简单的答案。当映射一个字符串数组时(最常见的情况),您不能确定它们是否唯一。因此,您不能使用值作为键。
您本可以使用数组索引作为键。但是,即使是非常静态的数组,也不可以这样做,因为这是不好的,而且 eslint 告诉我们不能这样做。
我建议编写一个简单的辅助函数,例如:
const mapArray = (source: string[]) => source.map((value, index) => ({id: index, value}))

这也意味着您需要添加一个状态来存储此映射,并编写一个效果,以在源更改时更新它。或者,您可以编写一个钩子,将其发布到npm上,并使这个世界变得更好一点。


0

React期望重复的项(兄弟节点)具有key属性,以便在后续渲染中区分项。这使它能够优化元素图的渲染,因为项会出现、消失和更改。

key应该是一个字符串,但React会将数字强制转换为字符串。你的示例非常简单,更正常的情况是数据包含一些唯一的项值(例如数据库记录ID),以便支持重复属性但仍然允许区分项。如果没有外部id属性,您可以决定将唯一标识符作为状态的一部分添加。

可以使用列表中的项索引,但这并不总是允许React优化项的渲染周期。这就是为什么如果您的数据包含唯一的项标识符,则不建议使用它。

React检查重复和缺少的键,以避免可能导致渲染周期效率降低的最明显的错误。

React的渲染优化对于非常长的列表确实会产生很大的影响。对于范围可能只有几个项的应用程序,不会有太多收益,因此数组索引可能是一个合理的解决方案。


0

使用index作为key是一种反模式,无论在任何情况下都会导致错误。

对于最佳答案 - 如果有人将索引1后面的数字更改为例如5,会发生什么?显示的数字将保持不变,因为key没有被更改

为了避免使用index作为原始数据类型的key所造成的问题,我建议使用复合键

我会这样编写NumbersList组件:

const NumbersList = ({ numbers }) => ( <ul> {numbers.map((number, index) => ( <li key={`${index}:${number}`}>{number * 2}</li> )} </ul> );


这是 NumbersList 组件的完整替换版本。只需注意命名(小写的 numbers 属性)。我在这里使用了对象解构,可以跳过输入 props 变量的步骤。 - Bohdan Shulha
这仍然存在碰撞的潜在可能性。 - Ryan Walker
不会。索引+数字对将是唯一的。给定数组[1, 1]将产生[“0:1”,“1:1”]组合键。即使有数百万个相同值的元素,也不会发生冲突。 - Bohdan Shulha

0
添加一个变量来改变索引:
{weekList.map((date, index) => {
  const x = index + 1 // <<<--- Just do it
  return(
  <React.Fragment key={x}>
    start: {date.startDate.format("YYYY-MM-DD")} end:{" "}
    {date.endDate.format("YYYY-MM-DD")}
  </React.Fragment>
)})}

你的回答可以通过提供更多支持性信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人能够确认你的回答是否正确。你可以在帮助中心找到关于如何撰写好回答的更多信息。 - Community

-3

React使用keys来识别哪些元素已更改、添加或删除。

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

不建议使用数组索引作为键,因为您可能会从数组中更新/删除元素,但索引将保持不变,因此React将无法识别更改并显示错误结果。

在您的代码中,没有任何唯一的ID用于数组元素,因此您唯一可以使用的是数组索引。

const Numbers = ['2', '4', '4', '8'];

const NumbersList = (props) => (
  <ul>
  {
  props.Numbers.map (
    (number, index) => <li key={index}>{number * 2}</li>
  )}
  </ul>
)

ReactDOM.render(
  <NumbersList Numbers = {Numbers} />,
  document.getElementById('root')
)
<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="root"></div>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接