React循环渲染什么时候不推荐使用index作为key,你真的理解了吗?

71 阅读3分钟

官方对于index作为key的观点是这样子的:

说的是对性能产生负面影响和组件的状态问题,我们可以通过以下代码来复现:

import React, { useState } from 'react';

const App = ()=>{
  const [items,setItems] = useState([
    {id:1747317088649,value:'hello'},
    {id:1747317088650,value:'world'}
  ])
  const addItem = ()=>{
    setItems(prevItems=>[{id:+new Date(),value:'nihao'},...prevItems])
  }
  
  return (
    <div>
      <h1>index作为key</h1>
      <button onClick={addItem}>添加Item</button>
      <div>
        {items.map((item,index)=>{
          return (
            <div key={index}>
              <input type='checkbox'/>
              <span>{item.id}:{item.value}</span>
            </div>
          )
        })}
      </div>
    </div>
  )
}

export default App

初始状态我们勾选一个item:

然后我们添加一个item:

我们可以看到checkbox似乎没有随着添加而进行改变到第二行,而是继续保留在第一行。

这其实就是一个主要的问题,主要是因为react根据key的变化来刷新key对应的dom树,这里我们使用index作为key可以理解为key的顺序是稳定的,中间任何一个key不会发生变化,这就导致当我们对items进行头增的时候,key不会发生任何变化,那么react在diff的时候看到的是这样子的对比:

    // 前
    <div key=0>
      <input type='checkbox'/>
      <span>1747317088649:hello</span>
    </div>
    <div key=1>
      <input type='checkbox'/>
      <span>1747317088650:world</span>
    </div>
    // 后
    <div key=0>
      <input type='checkbox'/>
      <span>1747317339267:nihao</span>
    </div>
    <div key=1>
      <input type='checkbox'/>
      <span>1747317088649:hello</span>
    </div>
    <div key=2>
      <input type='checkbox'/>
      <span>1747317088650:world</span>
    </div>

实际上react会认为:

  1. key=0<span>1747317088649:hello</span>变为了<span>1747317339267:nihao</span>,需要进行更新
  2. key=1<span>1747317088650:world</span>变为了<span>1747317088649:hello</span>,需要进行更新
  3. 多了个key=2,需要新增一个数据,进行更新

可以看到我们从始至终只对<span>进行了更新和新增了一个item,对于整个<div>的dom位置我们是没有进行改变的,所以这就会导致checkbox状态和item不匹配。
并且根据这个流程,我们假想如果key为唯一id的情况react在diff时候的对比:

    // 前
    <div key='0001'>
      <input type='checkbox'/>
      <span>1747317088649:hello</span>
    </div>
    <div key='0002'>
      <input type='checkbox'/>
      <span>1747317088650:world</span>
    </div>
    // 后
    <div key='0003'>
      <input type='checkbox'/>
      <span>1747317339267:nihao</span>
    </div>
    <div key='0001'>
      <input type='checkbox'/>
      <span>1747317088649:hello</span>
    </div>
    <div key='0002'>
      <input type='checkbox'/>
      <span>1747317088650:world</span>
    </div>

这时候react会认为:

  1. key='0001'上面多了一个key='0003',其他似乎没什么变化,需要在key='0001'上面新增一个item,进行更新。

这时候我们只进行了1次更新 性能有所提升,并且相对应的checkbox的状态也会跟随之前的元素,这样子就完美避免了之前所说的问题。

到这里,我们已经理解了使用index作为key在某些场景中会出现的问题。所以在实际使用中可以遵循以下最佳实践:

  1. 渲染的内容复杂或有交互性则需用唯一值(如id)作为key进行渲染
  2. 只作为展示的渲染,使用index并无太大问题。

参考:

legacy.reactjs.org/docs/lists-…

robinpokorny.com/blog/index-…

stackoverflow.com/questions/5…