谈谈 react 中的 key

前言

key-warn

若是你用过 react,而且曾经尝试遍历数组来渲染一个组件,就应该遇到过上面的提示。由于提示的等级为 Warning,而非 Error,因此不少开发同窗可能就不会去在乎,包括我本身。在前几天开发一个须要动态渲染的组件时,才发现的了 key 的妙用,也所以打算研究下 key 究竟是干什么用的。html

定义

Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity.react

key 是用来帮助 react 识别哪些内容被更改、添加或者删除。key 须要写在用数组渲染出来的元素内部,而且须要赋予其一个稳定的值。稳定在这里很重要,由于若是 key 值发生了变动,react 则会触发 UI 的重渲染。这是一个很是有用的特性。算法

key 的惟一性

在相邻的元素间,key 值必须是惟一的,若是出现了相同的 key,一样会抛出一个 Warning,告诉相邻组件间有重复的 key 值。而且只会渲染第一个重复 key 值中的元素,由于 react 会认为后续拥有相同 key 的都是同一个组件。数组

key 值不可读

虽然咱们在组件上定义了 key,可是在其子组件中,咱们并无办法拿到 key 的值,由于 key 仅仅是给 react 内部使用的。若是咱们须要使用到 key 值,能够经过其余方式传入,好比将 key 值赋给 id 等。ide

arr.map(item => <Component key={item.id} id={item.id} />)
复制代码

反模式

不少时候,咱们可能并无在遍历数组渲染组件的时候写上 key 的习惯,由于除了控制台报到一个 Warning,并不会有任何影响。由于赋 key 值这一步 react 帮咱们作了,默认使用的是遍历过程当中的 index 值。post

let arr = ['first', 'second'];

// list1 和 list2 是等价的
const list1 = arr.map(item => <p>{item}</p>);
const list2 = arr.map((item, index) => <p key={index}>{item}</p>);
复制代码

在上面的例子中,若是数组发生了变化,咱们须要在数组的末尾插入一个元素 arr.push('third'),react 通过 diff 后就会发现 :key 值为0和1的元素并无发生如何变化,因此 react 会认为, 最后须要在 UI 上发生变动,仅仅是插入一个 key 值为2的新元素。性能

可是若是咱们在数组的开头插入了一个新元素arr.unshift('zero'),react 通过 diff 后就会发现每个元素的 key 值都发生了变化,也是就说每一个元素都要从新渲染一次,虽然从结果来看,仅仅是在开头添加了一个元素而已。若是负责渲染的数组数据量较大的话,则会对性能形成较大的影响。与 react 使用的启发式算法是相悖的。spa

<!-- before -->
<p key="0">first</p>
<p key="1">second</p>

<!-- after -->
<p key="0">zero</p>
<p key="1">first</p>
<p key="2">second</p>
复制代码

所以,推荐的作法是每一个兄弟元素都加上一个稳定惟一的 key 值。code

应用

主要应用在 dynamic stateful components中,也就是须要动态渲染的子组件。component

下图是一个名为 Filters的组件,内部由一个个condition子组件组成,由 defaultValue负责初始化渲染。defaultValue的值可由组件生成,能够经过 onChange事件获取,默认不传为空数组。经过将以前保存好的值回传给组件,便可渲染须要的 Filters

const defalutValue = [{
  id: 1515134128441,
  tag: 'mobile',	// 手机号 决定最后须要使用的 input 输入框
  flag: [
    '身份标签',
    `{"code":"location_province_code","valueType":"area_province"}`
  ],                // 决定第一级 级联选框 的默认值
  operator: "neq",  // 操做符
  target: "00001"   // input 输入框的值
}, {
    ...
}]
复制代码

这里遇到的问题是,condition 子组件的内部已经比较复杂了,须要处理多种状况:

  1. 默认值的渲染
  2. 不一样筛选条件要对应不一样的赋值符以及条件框
  3. 按照级联的逻辑,父级的变动须要判断子级的状态是否须要改变

在上述状况中,若是condition要实现接收不一样的默认值来展现不一样的效果,则须要写一系列复杂的 componentWillReceiveProps生命周期。并且不能保证代码的可读性以及维护性,是一个很可怕的事情。

可是,若是你在用 defaultValue渲染每一个 Condition 的时候,给它加一个惟一稳定的 key 值,就能够完美解决这个问题。我在实现的过程当中,用了当前时间戳做为Condition的 key 值,保证其惟一稳定性。在删除、增长标签都能确保正确渲染。若是没有 key 值也没有写componentWillReceiveProps生命周期,在删除的时候就会发生渲染错误。

简而言之,改变 key 值来重渲染组件是一种——相较于复杂componentWillReceiveProps生命周期——十分低成本的方式。

参考连接

自由转载-非商用-非衍生-保持署名(创意共享3.0许可证

相关文章
相关标签/搜索