当谈起react中key属性,首先且唯一能想到的就是map遍历生成元素时得加上的那么个东西,除此之外key似乎不会用在别的地方了——这是我们对key的印象:它很重要但存在感低。
key是否真的只有这么一个用法?此处按下不表,先来回顾下key的原理及作用:
作用:
key的作用主要是用来减少没必要的diff算法对比,因为对于一个组件或者节点来说,只要父节点状态或者属性发生变化,该组件就会进行diff对比,即使该组件没变化。而如果为组件引入了key值,就可以在diff对比前先做一个校验,如果组件加上了key值,react就会在渲染时对该组件的身份进行校验,首先校验新旧组件的key值是不是一致,不一致的话,该组件直接销毁,然后再新建该组件;如果一致,则比较组件的属性是否发生变化,如果发生变化,则采用diff算法进行对比,然后得出差异对象,如果属性没发生变化,则认为该组件不需要改变。
以上就是key在react中对组件渲染的影响。
再来讲一下背景
以上对key
的剖析提到了两个关键词销毁和新建,这个特性其实拥有很要紧的作用,在某些特定的场景可以利用到。
为什么会突然关注到key的这个特性?这是因为有一天我在对react官网的基础知识点做”温故而知新“时候的看到有一节讲到了 state 依赖 props 的情况下如何处理。
(
这个截图官网链接是:https://zh-hans.reactjs.org/docs/react-component.html#constructor
截图中 【避免派生状态的博文】 链接中与本文主角 key 属性相关的地址是 :你可能不需要使用派生 state
)
它在这里提到了如下观点:
尽量不要使用props派生到state的方式来传递数据
不一定需要派生state:尝试一下 memoization?。
万不得已需要派生状态,也不要将this.state放在constructor中,因为constructor只会在生命周期中执行一次,无法响应后续props变化
派生state后需要结合使用
componentWillReceiveProps
或getDerivedStateFromProps
控制props
对state
的修改,否则组件不随props
变化而更新通过修改
key
以强制重置组件
官网中讲到了一个state依赖props而出现bug的场景,demo地址:点击查看。我描述如下:当我使用默认的同一个邮箱地址来登录不同的站点,在我编辑一个邮箱后我想切换到另一个站点,发现此时输入框中仍然保留了上次被编辑后的状态。很明显此时我期望的是输入框中应该重置为默认的那个邮箱地址。造成这个问题的原因是从one切换到two后UncontrolledEmailInput
组件接收到的props.defaultEmail
是一模一样的并没有发生改变,因此也就不会重置state。
官方对这个问题提出了两个解决方案:
- 一个是将输入组件对其父组件受控,value和change事件都由父组件来传入,避免子组件state与props冲突
- 另一个就是提到了本文主角使用
key
属性来销毁旧组件的方式
key的解决方案
使用key
属性解决方案我将官方demo整理如下:
import React, { Component, Fragment } from "react";
import ReactDom from "react-dom";
const fakeAccounts = [
{
id: 1,
name: "One",
email: "fake.email@example.com"
},
{
id: 2,
name: "Two",
email: "fake.email@example.com"
}
];
class UncontrolledEmailInput extends Component {
state = {
email: this.props.defaultEmail
};
handleChange = event => {
this.setState({ email: event.target.value });
};
render() {
return (
<label>
Email: <input onChange={this.handleChange} value={this.state.email} />
</label>
);
}
}
class AccountsList extends Component {
state = {
selectedIndex: 0
};
render() {
const { accounts } = this.props;
const { selectedIndex } = this.state;
const selectedAccount = accounts[selectedIndex];
return (
<Fragment>
<h1>
This demo illustrates resetting an uncontrolled component with a key
</h1>
<blockquote>First, make an edit to the account "One" email.</blockquote>
<UncontrolledEmailInput
key={selectedAccount.id} // 关键之处,可删掉它来测试
defaultEmail={selectedAccount.email}
/>
<blockquote>Next, select account "Two" below.</blockquote>
<p>
Accounts:
{this.props.accounts.map((account, index) => (
<label key={account.id}>
<input
type="radio"
name="account"
checked={selectedIndex === index}
onChange={() => this.setState({ selectedIndex: index })}
/>{" "}
{account.name}
</label>
))}
</p>
</Fragment>
);
}
}
ReactDom.render(
<AccountsList accounts={fakeAccounts} />,
document.getElementById("root")
);
它将UncontrolledEmailInput
组件添加了key
属性,利用key
对组件渲染的特殊特性,当切换one
与two
按钮时传入到UncontrolledEmailInput
组件的key值发生变化,从而每次会销毁然后重建UncontrolledEmailInput
组件,因而UncontrolledEmailInput
组件中输入框的内容肯定都回到最初。
完美解决!仅仅一个很小的知识点就能解决这个小小的但又棘手的问题
本篇除了key
,也让自己回顾了下派生state、不常用的生命周期如getDerivedStateFromProps
和UNSAFE_componentWillReceiveProps
、受控组件等等一些知识点,每次回顾老的知识点,总发现很多知识又变得生疏了,这还真是个问题啊。