React key概述
key的作用
key 帮助 React 识别哪些元素改变了,比如被添加或删除。因此你应当给数组中的每一个元素赋予一个确定的标识。它是一个特殊的属性,它是出现不是给开发者用的(例如你为一个组件设置key之后不能获取组件的这个key props),而是给react自己用的。
简单来说,react利用key来识别组件,它是一种身份标识标识,就像我们的身份证用来辨识一个人一样。每个key对应一个组件,相同的key react认为是同一个组件,这样后续相同的key对应组件都不会被创建。例如下面代码:
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) =>
<li key={number.toString()}>
{number}
</li>
);
一个元素的 key 最好是这个元素在列表中拥有的一个独一无二的字符串。通常,我们使用来自数据 id 来作为元素的 key:
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
);
当元素没有确定 id 的时候,万不得已你可以使用元素索引 index 作为 key:
const todoItems = todos.map((todo, index) =>
// Only do this if items have no stable IDs
<li key={index}>
{todo.text}
</li>
);
由于列表项目的顺序可能会变化,我们不建议使用索引来用作 key 值,因为这样做会导致性能变差,还可能引起组件状态的问题。
例如,当你向列表中添加一条数据或者移除某条数据时。如果该键与之前的相同,React会认为DOM元素表示的组件和以前是一样的。
举个例子:
-
组件重新render得到新的虚拟dom;
-
新老两个虚拟dom进行diff,新老版的都有key=0的组件,react认为同一个组件,则只可能更新组件;
-
然后比较其children,发现内容的文本内容不同,而input组件并没有变化,这时触发组件的componentWillReceiveProps方法,从而更新其子组件文本内容;
-
因为组件的children中input组件没有变化,其又与父组件传入的props没有关联,所以input组件不会更新(即其componentWillReceiveProps方法不会被执行),导致用户输入的值不会变化。
这就是index作为key存在的问题,所以不要使用index作为key。
可以使用索引作为key值的三种情况:
-
列表和项目是静态的(它们不是计算的,也不会改变);
-
列表中的项目没有id;
-
该列表永远不会重新排序或过滤。
列表没有id时的解决方式:
- 创建元素时增加一个自增id属性
使用一个全局的index来确保任何两个列表项的id不同
todoCounter = 1;
function createNewTodo(text) {
return {
completed: false,
id: todoCounter++,
text
}
}
- 使用shortid库来产生id
强烈推荐一个npm包shortid, 它可以快速的生成一系列‘短的无序的对url友好的 唯一的’ id,下面是示例代码:
var shortid = require('shortid');
function createNewTodo(text) {
return {
completed: false,
id: shortid.generate(),
text
}
}
更多请参考《Index as a key is an anti-pattern》
使用key时注意点
- 元素的 key 只有放在就近的数组上下文中才有意义
一个好的经验法则是:在 map() 方法中的元素需要设置 key 属性。
- key 只是在兄弟节点之间必须唯一
数组元素中使用的 key 在其兄弟节点之间应该是独一无二的。然而,它们不需要是全局唯一的。当我们生成两个不同的数组时,我们可以使用相同的 key 值