在开发react程序时我们经常会遇到这样的警告,然后就会想到:哦!循环子组件忘记加key了~

出于方便,有时候会不假思索的使用循环的索引作为key,但是这样真的好吗?什么样的值才是key的最佳选择?

为了弄明白,本文将从三个方面来分析"key":

1.为什么要使用key

2.使用index做key存在的问题

3.正确的选择key

1.为什么要使用key

react官方文档是这样描述key的:

Keys可以在DOM中的某些元素被增加或删除的时候帮助React识别哪些元素发生了变化。因此你应当给数组中的每一个元素赋予一个确定的标识。

react的diff算法是把key当成唯一id然后比对组件的value来确定是否需要更新的,所以如果没有key,react将不会知道该如何更新组件。

你不传key也能用是因为react检测到子组件没有key后,会默认将数组的索引作为key。

react根据key来决定是销毁重新创建组件还是更新组件,原则是:

  • key相同,组件有所变化,react会只更新组件对应变化的属性。
  • key不同,组件会销毁之前的组件,将整个组件重新渲染。

2.使用index做key存在的问题

2.1 受控组件

单纯的展示组件比如span,这些组件是受控组件,意味着他们的值将是我们给定好的。

如果子组件只是受控组件,使用index作为key,可能表面上不会有什么问题,实际上性能会受很大的影响。例如下面的代码:

// ['张三','李四','王五']=>
<ul><li key="0">张三</li><li key="1">李四</li><li key="2">王五</li>
</ul>
// 数组重排 -> ['王五','张三','李四'] =>
<ul><li key="0">王五</li><li key="1">张三</li><li key="2">李四</li>
</ul>

当元素数据源的顺序发生改变时,对应的:

key为0,1,2的组件都发生了变化,三个子组件都会被重新渲染。(这里的重新渲染不是销毁,因为key还在)

相反,我们使用唯一id作为key:

// ['张三','李四','王五']=>
<ul><li key="000">张三</li><li key="111">李四</li><li key="222">王五</li>
</ul>
// 数组重排 -> ['王五','张三','李四'] =>
<ul><li key="222">王五</li><li key="000">张三</li><li key="111">李四</li>
</ul>

根据上面的更新原则,子组件的值和key均未发生变化,只是顺序发生改变,因此react只是将他们做了移动,并未重新渲染。

2.2 非受控组件

像input这样可以由用户任意改变值,不受我们控制的组件,在使用了index作为key时可能会发生问题,看如下的栗子:

子组件:

  render() {return (<div><p >值:{this.props.value}</p><input /></div>);}
}

父组件

{
this.state.data.map((element, index) => {return <Child value={element} key={index} />})
}

我们在前两个输入框分别输入对应的值:

然后在头部添加一个元素:

很明显,这个结果并不符合我们的预期,我们来分析一下发生了什么:

<div key="0"><p >值:0</p><input />
</div>
<div key="1"><p >值:1</p><input />
</div>
<div key="2"><p >值:2</p><input />
</div>

变化后:

<div key="0"><p >值:5</p><input />
</div>
<div key="1"><p >值:0</p><input />
</div>
<div key="2"><p >值:1</p><input />
</div>
<div key="3"><p >值:2</p><input />
</div>

可以发现:key 0,1,2并没有发生改变,根据规则,不会卸载组件,只会更新改变的属性。

react只diff到了p标签内值的变化,而input框中的值并未发生改变,因此不会重新渲染,只更新的p标签的值。

当使用唯一id作为key后:

<div key="000"><p >值:0</p><input />
</div>
<div key="111"><p >值:1</p><input />
</div>
<div key="222"><p >值:2</p><input />
</div>

变化后:

<div key="555"><p >值:5</p><input />
</div>
<div key="000"><p >值:0</p><input />
</div>
<div key="111"><p >值:1</p><input />
</div>
<div key="222"><p >值:2</p><input />
</div>

可以很明显的发现:key为 111,222,333的组件没有发生任何改变,react不会更新他们,只是新插入了子组件555,并改变了其他组件的位置。

3.正确的选择key

3.1 纯展示

如果组件单纯的用于展示,不会发生其他变更,那么使用index或者其他任何不相同的值作为key是没有任何问题的,因为不会发生diff,就不会用到key。

3.2 推荐使用index的情况

并不是任何情况使用index作为key会有缺陷,比如如下情况:

你要分页渲染一个列表,每次点击翻页会重新渲染:

使用唯一id:

第一页
<ul><li key="000">张三</li><li key="111">李四</li><li key="222">王五</li>
</ul>
第二页
<ul><li key="333">张三三</li><li key="444">李四四</li><li key="555">王五五</li>
</ul>

翻页后,三条记录的key和组件都发生了改变,因此三个子组件都会被卸载然后重新渲染。

使用index:

第一页
<ul><li key="0">张三</li><li key="1">李四</li><li key="2">王五</li>
</ul>
第二页
<ul><li key="0">张三三</li><li key="1">李四四</li><li key="2">王五五</li>
</ul>

翻页后,key不变,子组件值发生改变,组件并不会被卸载,只发生更新。

3.3 子组件可能发生变更/使用了非受控组件

大多数情况下,使用唯一id作为子组件的key是不会有任何问题的。

这个id一定要是唯一,并且稳定的,意思是这条记录对应的id一定是独一无二的,并且永远不会发生改变。

不推荐使用math.random或者其他的第三方库来生成唯一值作为key。

因为当数据变更后,相同的数据的key也有可能会发生变化,从而重新渲染,引起不必要的性能浪费。

如果数据源不满足我们这样的需求,我们可以在渲染之前为数据源手动添加唯一id,而不是在渲染时添加。

react中key的正确使用方式相关推荐

  1. React中组件间通信的方式

    React中组件间通信的方式 React中组件间通信包括父子组件.兄弟组件.隔代组件.非嵌套组件之间通信. Props props适用于父子组件的通信,props以单向数据流的形式可以很好的完成父子组 ...

  2. react中key的作用

    背景: 如果为父节点添加多个相同的子节点时,不添加key属性,会报错但同时也会渲染出dom,渲染出dom其实是证明能从差异对象中渲染出真实dom,但报错的原因是因为这种写法会影响渲染的性能,不利于生层 ...

  3. react中key的作用 1

    1.react中的key有什么作用? 简单的来说就是为了提高diff的同级比较的效率,避免原地复用带来的副作用, vue和react本身都是采用diff的算法vue采用的是更为细粒的更新组件方式,即是 ...

  4. React中过渡动画的编写方式

    文章目录 React的过渡动画 过渡动画库的介绍 CSSTransition(掌握) SwitchTransition(了解) TransitionGroup(了解) React的过渡动画 过渡动画库 ...

  5. mysql正确打开方式_MySQL中MVCC的正确打开方式

    最近在学习MySQL中的MVCC,看了网上的各种版本,什么创建版本号.删除版本号,一开始看的时候,好像很对的样子,但实际上很多都是错误的.经过好几天的查阅对比,在几篇博客的帮助下,才算是觉得正确理解了 ...

  6. mysql config.xml_generatorConfig-mysql.xml中连接数据库的正确书写方式。

    在做spring boot开发时,刚开始实训的时候一直做的数据库表的增删改查,但是在MybatisGenerator自动生成java文件时,在XML文件中由于书写不正确一直连接不上数据库. 刚上手项目 ...

  7. CSS中em的正确打开方式

    为什么说"通常情况下1em=16px"? 用户的浏览器默认渲染的文字大小是"16px",换句话说,Web页面中"body"及其子元素的文字大 ...

  8. MySQL中MVCC的正确打开方式(源码佐证)

    序 最近在学习MySQL中的MVCC,看了网上的各种版本,什么创建版本号.删除版本号,一开始看的时候,好像很对的样子,但实际上很多都是错误的.经过好几天的查阅对比,在几篇博客的帮助下,才算是觉得正确理 ...

  9. 微信小程序中weui的正确打开方式

    最近接触小程序,上手写了一些代码之后,思考是不是小程序也有相关的ui框架呢,于是百度了一下,发现ui框架还真的不少,主流的当属weui,那么问题来了,我搜了很多,但是始终没有找到组件开发文档,有的都是 ...

最新文章

  1. 独家 | XGBoost介绍:用监督学习来预测期望寿命
  2. svn 代码管理工具
  3. Py之Seaborn:数据可视化Seaborn库的柱状图、箱线图(置信区间图)、散点图/折线图、核密度图/等高线图、盒形图/小提琴图/LV多框图的组合图/矩阵图实现
  4. 分享我第一次做项目的感受
  5. comsol临时文件夹中有不支持的字符_文件名中不能包含的字符
  6. Onboard,迷人的引导页样式制作库
  7. base | 使用apply族函数进行向量化运算
  8. 测试点击屏幕次数的软件_软件测试工程师面试如何回答登录功能怎么进行测试?...
  9. 一致性hash算法的应用研究学习
  10. FreeBSD从源码升级系统--重新编译
  11. c语言编写的车道线检测程序,opencv车道线检测的实现方法
  12. 仿微信的语音点击播放时的语音图标动画效果
  13. 五笔86版字根图程序
  14. 【python】案例十 字符串中的逆天函数
  15. 蚂蚁金服杨军:蚂蚁数据分析平台的演进及数据分析方法的应用
  16. java 制作 winrar,windows 上java调用winrar压缩文件为rar 格式
  17. 钌碳Ru/NC7440-18-8制备碳化铁嵌入式碳复合材料
  18. Go语言代码实现数字签名过程
  19. 黑群晖升级后无法找到的方法
  20. “98五笔字型输入法”大批量造词 (转)

热门文章

  1. mysql binlog 备份_偷偷的删表删库,跑路之前,尝试用binlog恢复MySQL数据
  2. 东方终焉组审核页可做引导页
  3. github-markdown-css 使用简解,markdown文案格式优化(笔记)
  4. CRMEB知识付费系统v1.4.4源码
  5. 最细致的Spring Boot结合Vue前后端分离项目打包部署步骤(搭配Nginx)
  6. 字母e和i如何发音?
  7. .NET 项目开发总结
  8. Android模拟器genymotion的安装和使用
  9. 非负矩阵分解推导(NMF)
  10. 空洞卷积(dilated convolution)