官方对于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会认为:
key=0
处<span>1747317088649:hello</span>
变为了<span>1747317339267:nihao</span>
,需要进行更新key=1
处<span>1747317088650:world</span>
变为了<span>1747317088649:hello</span>
,需要进行更新- 多了个
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会认为:
- 在
key='0001'
上面多了一个key='0003'
,其他似乎没什么变化,需要在key='0001'
上面新增一个item,进行更新。
这时候我们只进行了1次更新 性能有所提升,并且相对应的checkbox的状态也会跟随之前的元素,这样子就完美避免了之前所说的问题。
到这里,我们已经理解了使用index作为key在某些场景中会出现的问题。所以在实际使用中可以遵循以下最佳实践:
- 渲染的内容复杂或有交互性则需用唯一值(如id)作为key进行渲染
- 只作为展示的渲染,使用index并无太大问题。
参考: