React--key值详解
项目中遇到不少的坑,例如组件的key影响加载,或者警告数组遍历子元素要有一个唯一的key值等,今天汇总说一下: 参考博客 1、key的作用react中的key属性,它是一个特殊的属性,它是出现不是给开发者用的(例如你为一个组件设置key之后不能获取组件的这个key props),而是给react自己用的。 那么react是怎么用key的呢?react的作者之一Paul O’Shannessy有提到:
简单来说,react利用key来识别组件,它是一种身份标识标识,就像我们的身份证用来辨识一个人一样。每个key对应一个组件,相同的key react认为是同一个组件,这样后续相同的key对应组件都不会被创建。例如下面代码: //this.state.users内容。注意:李四和王五的id相同!!!
this.state = {
users: [{id:1,name: '张三'},{id:2,name: '李四'},{id: 2,name: "王五"}],....//省略
}
render()
return(
<div>
<h3>用户列表</h3>
{this.state.users.map(u => <div key={u.id}>{u.id}:{u.name}</div>)}
</div>
)
);
上面代码在dom渲染挂载后,用户列表只有张三和李四两个用户,王五并没有展示处理,主要是因为 react根据key认为李四和王五是同一个组件(李四和王五的key值相同),导致第一个被渲染,后续的会被丢弃掉。 这样,有了key属性后,就可以与组件建立了一种对应关系,react根据key来决定是销毁重新创建组件还是更新组件。
另外需要指明的是:
2、key的使用场景:(1)在项目开发中,key属性的使用场景最多的还是由数组动态创建的子组件的情况,需要为每个子组件添加唯一的key属性值。 那么,为何由数组动态创建的组件必须要用到key属性呢?这跟数组元素的动态性有关。 拿上述用户列表的例子来说,看一下babel对上述代码的转换情况: // 转换前
const element = (
<div>
<h3>用户列表</h3>
{[<div key={1}>1:张三</div>,<div key={2}>2:李四</div>]}
</div>
);
// 转换后
"use strict";
var element = React.createElement(
"div",null,React.createElement("h3","用户列表"),[
React.createElement("div",{ key: 1 },"1:张三"),React.createElement("div",{ key: 2 },"2:李四")
]
);
从babel转换后React.createElement中的代码可以看出,其它元素之所以不是必须需要key是因为不管组件的state或者props如何变化,这些元素始终占据着React.createElement固定的位置,这个位置就是天然的key。 而由数组创建的组件可能由于动态的操作导致重新渲染时,子组件的位置发生了变化,例如上面用户列表子组件新增一个用户,上面两个用户的位置可能变化为下面这样: var element = React.createElement(
"div",null,React.createElement("h3",[
React.createElement("div",{ key: 3 },"1:王五"),React.createElement("div","2:张三"),"3:李四")
]
);
可以看出,数组创建子组件的位置并不固定,动态改变的;这样有了key属性后,react就可以根据key值来判断是否为同一组件。 (2)另外,还有一种比较常见的场景:为一个有复杂繁琐逻辑的组件添加key后,后续操作可以改变该组件的key属性值,从而达到先销毁之前的组件,再重新创建该组件。 相关案例可查看上篇博客:wangEditor富文本编辑器+react+antd的使用 3、key的最佳实践由数组创建的子组件必须有key属性,否则的话你可能见到下面这样的warning: Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of `ServiceInfo`. See https://fb.me/react-warning-keys for more information.
可能你会发现,这只是warning而不是error,它不是强制性的,为什么react不强制要求用key而报error呢?其实是强制要求的,只不过react为按要求来默认上帮我们做了,它是以数组的index作为key的。
在list数组中,用key来标识数组创建子组件时,若数组的内容只是作为纯展示,而不涉及到数组的动态变更,其实是可以使用index作为key的。 但是,若涉及到数组的动态变更,例如数组新增元素、删除元素或者重新排序等,这时index作为key会导致展示错误的数据。本文开始引入的例子就是最好的证明。 {this.state.data.map((v,idx)=><Item key={idx} v={v} />)}
// 开始时:['a','b','c']=>
<ul>
<li key="0">a <input type="text"/></li>
<li key="1">b <input type="text"/></li>
<li key="2">c <input type="text"/></li>
</ul>
// 数组重排 -> ['c','a'] =>
<ul>
<li key="0">c <input type="text"/></li>
<li key="1">b <input type="text"/></li>
<li key="2">a <input type="text"/></li>
</ul>
上面实例中在数组重新排序后,key对应的实例都没有销毁,而是重新更新。具体更新过程我们拿key=0的元素来说明, 数组重新排序后:
这就是index作为key存在的问题,所以不要使用index作为key。
在数组中生成的每项都要有key属性,并且key的值是一个永久且唯一的值,即稳定唯一。 在理想情况下,在循环一个对象数组时,数组的每一项都会有用于区分其他项的一个键值,相当数据库中主键。这样就可以用该属性值作为key值。但是一般情况下可能是没有这个属性值的,这时就需要我们自己保证。 在保证数组每项的唯一的标识时,还需要保证其值的稳定性,不能经常改变。key的值要保持稳定且唯一,不能使用random来生成key的值。如下述代码: {
this.state.data.map(el=><MyComponent key={Math.random()}/>)
}
上面代码中中MyComponent的key值是用Math.random随机生成的,虽然能够保持其唯一性,但是它的值是随机而不是稳定的,在数组动态改变时会导致数组元素中的每项都重新销毁然后重新创建,有一定的性能开销;另外可能导致一些意想不到的问题出现。所以:
所以,在不能使用random随机生成key时,我们可以像下面这样用一个全局的 var localCounter = 1;
this.data.forEach(el=>{
el.id = localCounter++;
});
//向数组中动态添加元素时,
function createUser(user) {
return {
...user,id: localCounter++
}
}
4、key的其他注意事项
//MyComponent
...
render() {//error
<div key={{item.key}}>{{item.name}}</div>
}
...
//right
<MyComponent key={{item.key}}/>
{
this.state.type ?
<div><Son_1/><Son_2/></div>
: <div><Son_2/><Son_1/></div>
}
例如上面代码中, {
this.state.type ?
<div><Son_1 key="1"/><Son_2 key="2"/></div>
: <div><Son_2 key="2" /><Son_1 key="1"/></div>
}
这样, 5、参考 React-key详解 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- 多范式编程语言-以Swift为例
- Eslint 超简单入门教程
- [转载Oracle官方中文博客]关于RunQ过高引起的latch等待问题
- cocos2d-x3.2中在Eclipse中java.lang.NullPointerException
- 何时在Ruby中使用符号
- c# – foreach语句不能对’getenumerator’的公共定义类型的
- XML即可扩展标记语言(Extensible Markup Language)
- ios – ErrorCode:AccessDenied,消息:AWS身份验证需要有效
- VB.NET让webbrowser控件中JS脚本错误最新方法(2013-09-16)
- 设计模式六大原则(1):单一职责原则