在React中,为什么列表组件中列表项需要key?

2,008 阅读4分钟

List列表组件在我们的项目中是最为常见的一种组件形式。你可以在比如刷微博抖音、购物车等等,这种列表状的场景很多很多。想象一下,如果带有大量视频或图片的应用中,当你滚动的时候,程序会不断的加载它们,这可能会对应用的性能产生影响。

因为性能是非常重要的一个方面,所以在渲染列表时,你需要确保它们是为最佳性能而设计的。

那么,在React中,你知道在渲染列表的时候,每个列表项都需要添加一个唯一的key。接下来让我们去了解React中的列表和Key,以及如何以正确的方式实现它。

写一个简单的List组件

    renderList = (arr) => {
      const list = arr.map((item) => <li>{item.name}</li> )
      return (
        <ul>{list}</ul>
      )
    }
    
    render() {
      const myList = [
       {name: '李华'},
       {name: '张飞'}
      ]
      return (
        <div>
          {this.renderList(myList)}
        <div/>
      )
    }

上面写了一个方法组件,传入一个list,页面输出以下内容:

  • 李华
  • 张飞

但是当你运行这段代码时,你会发现控制台React报出警告:

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

意思就是渲染list的每一个列表项没有加上唯一的key。

那么我们该如何在List组件中使用key

key呢其实就是提高React应用(组件)性能所必须的标识,它帮助React识别哪些项已更改(添加/删除/重新排序)。所以要为数组中的每个元素提供唯一标识,需要一个key。

为了更好地理解这一点,我们开始重构之前写的代码片段(包含key)。

    renderList = (arr) => {
      const list = arr.map((item) => 
        <li key={u.id}>{item.name}</li>
      )
      return (
        <ul>{list}</ul>
      )
    }
    
    render() {
      const myList = [
       {id:'a',name: '李华'},
       {id:'b',name: '张飞'}
      ]
      return (
        <div>
          {this.renderList(myList)}
        <div/>
      )
    }

在上面的代码片段中,你可以注意到我在每个li标签添加了key这个属性。还有数组中的每项都有一个与之关联的id,因此,每个li的key都被指定为数组中对应各项的id。这是为List中的列表项分配唯一key的最佳方法。

我可以只使用index作为key吗?(看情况)

为什么说看情况呢,请查看Robin Pokorny的这篇文章关于索引作为key是反模式的做法。 你可能一脸黑人问号 ,为什么我们不在循环数组时只使用索引作为key。 尽管许多开发者(包括我在内哈哈哈哈)在代码中都是这样做,但它并不一定是符合理想的。 React建议你不要使用索引作为key,因为它可能会对性能产生负面影响,并可能导致一些不稳定的组件行为。

const list = arr.map((item, index) =>
  // 只有在item没有稳定id时才这样做
  <li key={index}>
    {item.name}
  </li>
);

当索引作为key时,list的重新排序,或从list中添加和删除项可能会导致组件状态出现问题,重新排序项的索引会改变它,组件状态可能会混淆,而且会将旧key作用于不同的组件实例。

因此,我们要避免这种做法,并确保生成唯一的id指定为key。

在某些情况下,将索引指定为key是可以接受的。

哪些情况下使用索引作为key是可以接受的呢

  1. 如果您的列表是静态的,不会更改的。
  2. 该列表不会被过滤(从列表中添加/删除项目)。
  3. list数据中的item没有id。

如果所有这些情况都符合,那么你可以使用索引作为key。

注意:使用索引作为键可能会导致组件中出现潜在的意外行为

key必须是唯一的,但只是在其兄弟组件之间

请记住,尽管每个项的key都必须是唯一的,但此规则仅适用于数组。 换句话说,数组中的每个项目都需要具有唯一key,但它不必是全局唯一的。 可以在不相关的其他几个组件和列表中使用相同的key。

key是不会自动作为prop传递给组件的

在下面的示例中你可以看到我传递了item.id给key和id属性给MyComponent组件, 但是MyComponent不会把key作为属性通过props进行传递。如果你想使用key进行某些计算,那么你需要将它作为另一个属性传递。

const content = items.map((item) =>
  <MyComponent
    key={item.id}
    id={item.id} />
)

在这个例子中,MyComponent可以读取props.id但不能读取props.key。

最后的结论

回顾一下今天的重点:

  1. 列表性能消耗很繁重,需要谨慎使用。
  2. 确保列表中的每个项目都有唯一的key。
  3. 除非您确定列表是静态列表(没有添加/重新排序/删除列表),否则优先不使用索引作为key。
  4. 切勿使用像Math.random()这样的不稳定方法来生成key。
  5. 如果使用不稳定的key,React将遇到性能下降和意外行为。