Littlest things

React中的Keys

说在开头

React的性能优化的方案有很多如:

  • PureComponent
  • shouldComponentUpdate
  • Immutable
  • Stateless component
  • Key

本文将简单的聊聊keys,为什么需要keys,那些情况是糟糕的。

通过一个案例引出Keys

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import React from 'react';
import ReactDOM from 'react-dom';
const ListItem = (props) =>
<li>{props.item.content}</li>
const List = (props) =>
<ul>
{props.data.map((item) => {
return <ListItem item={item}/>
})}
</ul>
const data = [
{id: 1, content: 'hehe'}, {id: 2, content: 'haha'}, {id: 3, content: "heihei"}
]
// Render the main component into the dom
ReactDOM.render(<List data={data}/>, document.getElementById('app'));

上面的代码简单的声明的两个无状态组件ListListItem,然后组成一个列表。

注:

当你的组件仅仅只是展示数据,没有一个state属性,React推荐使用无状态组件。如果List组件有一些交互的逻辑,那么List就不能使用无状态组件。

List组件中通过map函数,进行ListItem的渲染,此时你可以在console里面看到一个错误

如果没有给动态的子组件添加一项key prop,则会报一个警告。
这个警告指的是,如果子组件是一个数组或者迭代器的话,那么必须有一个唯一的key prop

Key Prop的作用

如果需要渲染一个有100列的列表,而且每隔几秒就会更新一次,启动一部分只是位置变化,还有一部分是完全更新,一部分从列表中清楚。
此时key就发挥了作用,他是用来标识当前项的唯一性的props。
很多时候我们会看到这样的代码:

1
2
3
4
5
6
7
8
9
// 糟糕的
// 如果把index设置为key,这么做确实没有了警告,但是这种做法非常低效。
// 这个key是每次用来Virtual Dom diff的,到此每次更新都会重新渲染
const List = (props) =>
<ul>
{props.data.map((item,index) => {
return <ListItem key={index} item={item}/>
})}
</ul>
1
2
3
4
5
6
7
8
9
10
// 糟糕的
const ListItem = (props) =>
<li key={props.id}>{props.item.content}</li>
const List = (props) =>
<ul>
{props.data.map((item) => {
return <ListItem item={item}/>
})}
</ul>

正确的做法是,将key换成item.id,但是有一个必要条件是item.id是唯一的。

当Key不唯一

key有一个原则,就是唯一的,独一无二的。

当key相同时,React会怎么渲染呢?他只会渲染第一个相同的key项。

1
2
3
4
5
6
7
8
// 糟糕的
// 只会出现 hehe 其他的都没有显示
const List = (props) =>
<ul>
{props.data.map((item) => {
return <ListItem key={1} item={item}/>
})}
</ul>

则会出现这个警告:

1
2
3
Warning: flattenChildren(...): Encountered two children with the same key, `1`. Child keys must be unique; when two children share a key, only the first child will be used.
in ul (created by List)
in List