當談起react中key屬性,首先且唯一能想到的就是map遍歷生成元素時得加上的那么個東西,除此之外key似乎不會用在別的地方了——這是我們對key的印象:它很重要但存在感低。
key是否真的只有這么一個用法偎痛?此處按下不表,先來回顧下key的原理及作用:
作用:
key的作用主要是用來減少沒必要的diff算法對比,因為對于一個組件或者節(jié)點來說塞颁,只要父節(jié)點狀態(tài)或者屬性發(fā)生變化,該組件就會進行diff對比吸耿,即使該組件沒變化祠锣。而如果為組件引入了key值,就可以在diff對比前先做一個校驗咽安,如果組件加上了key值伴网,react就會在渲染時對該組件的身份進行校驗,首先校驗新舊組件的key值是不是一致妆棒,不一致的話澡腾,該組件直接銷毀,然后再新建該組件糕珊;如果一致动分,則比較組件的屬性是否發(fā)生變化,如果發(fā)生變化红选,則采用diff算法進行對比澜公,然后得出差異對象,如果屬性沒發(fā)生變化纠脾,則認為該組件不需要改變玛瘸。
以上就是key在react中對組件渲染的影響。
再來講一下背景
以上對key
的剖析提到了兩個關(guān)鍵詞銷毀和新建苟蹈,這個特性其實擁有很要緊的作用糊渊,在某些特定的場景可以利用到。
為什么會突然關(guān)注到key的這個特性慧脱?這是因為有一天我在對react官網(wǎng)的基礎(chǔ)知識點做”溫故而知新“時候的看到有一節(jié)講到了 state 依賴 props 的情況下如何處理渺绒。
(
這個截圖官網(wǎng)鏈接是:https://zh-hans.reactjs.org/docs/react-component.html#constructor
截圖中 【避免派生狀態(tài)的博文】 鏈接中與本文主角 key 屬性相關(guān)的地址是 :你可能不需要使用派生 state
)
它在這里提到了如下觀點:
盡量不要使用props派生到state的方式來傳遞數(shù)據(jù)
不一定需要派生state:嘗試一下 memoization?。
萬不得已需要派生狀態(tài)宗兼,也不要將this.state放在constructor中躏鱼,因為constructor只會在生命周期中執(zhí)行一次,無法響應(yīng)后續(xù)props變化
派生state后需要結(jié)合使用
componentWillReceiveProps
或getDerivedStateFromProps
控制props
對state
的修改殷绍,否則組件不隨props
變化而更新通過修改
key
以強制重置組件
官網(wǎng)中講到了一個state依賴props而出現(xiàn)bug的場景染苛,demo地址:點擊查看。我描述如下:當我使用默認的同一個郵箱地址來登錄不同的站點主到,在我編輯一個郵箱后我想切換到另一個站點茶行,發(fā)現(xiàn)此時輸入框中仍然保留了上次被編輯后的狀態(tài)。很明顯此時我期望的是輸入框中應(yīng)該重置為默認的那個郵箱地址登钥。造成這個問題的原因是從one切換到two后UncontrolledEmailInput
組件接收到的props.defaultEmail
是一模一樣的并沒有發(fā)生改變畔师,因此也就不會重置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} // 關(guān)鍵之處,可刪掉它來測試
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值發(fā)生變化,從而每次會銷毀然后重建UncontrolledEmailInput
組件献幔,因而UncontrolledEmailInput
組件中輸入框的內(nèi)容肯定都回到最初懂傀。
完美解決趾诗!僅僅一個很小的知識點就能解決這個小小的但又棘手的問題
本篇除了key
蜡感,也讓自己回顧了下派生state、不常用的生命周期如getDerivedStateFromProps
和UNSAFE_componentWillReceiveProps
恃泪、受控組件等等一些知識點郑兴,每次回顧老的知識點,總發(fā)現(xiàn)很多知識又變得生疏了贝乎,這還真是個問題啊情连。