詳解React中key的作用
要了解React中key的作用,可以從key的取值入手,key的取值可以分為三種,不定值、索引值、確定且唯一值
在下面的代碼中,key的取值是不定值(Math.random())
問題: 點(diǎn)擊按鈕的時(shí)候,span的顏色會變成紅色嗎?
import React, { useState } from 'react';
function App() {
const [initMap, setInitMap] = useState([1,2,3,4]);
const handleClick = () => {
setInitMap([1,2,3,4])
var spanEle = document.getElementsByTagName('span');
Array.from(spanEle).map(it => it.style.color = 'red')
}
return (
<div className="App" id="app">
{
initMap.map((it,index) => <div key={Math.random()}><span>color</span></div>)
}
<button onClick={() => handleClick()}></button>
</div>
);
}
export default App;
答案是:不會
這個(gè)問題涉及react渲染機(jī)制和diff算法
官網(wǎng)中對于diff有如下規(guī)則:
- 當(dāng)元素類型變化時(shí),會銷毀重建
- 當(dāng)元素類型不變時(shí),對比屬性
- 當(dāng)組件元素類型不變時(shí),通過props遞歸判斷子節(jié)點(diǎn)
- 遞歸對比子節(jié)點(diǎn),當(dāng)子節(jié)點(diǎn)是列表時(shí),通過key和props來判斷。若key一致,則進(jìn)行更新,若key不一致,就銷毀重建
分析上述問題:
當(dāng)點(diǎn)擊按鈕時(shí),setInitMap([1,2,3,4])會造成渲染,渲染時(shí)會生成新的虛擬dom,但此時(shí)獲取到的span元素是之前的元素(因?yàn)閟etInitMap是異步執(zhí)行的),所以新舊dom會做對比
在initMap.map((it,index) => <div key={Math.random()}><span>color</span></div>)這段代碼中
這里的div是列表,對比第四條diff規(guī)則,react會根據(jù)key來判斷是否更新真實(shí)dom。key= {Math.random()},新舊dom的值不一致,就會重新生成div。而我們是給更新之前的元素加了紅色的樣式,所以重新創(chuàng)建的元素上不會有這個(gè)樣式,效果如下

第二種情況:key的取值為索引值
上面我們分析的結(jié)果是,因?yàn)閗ey的變化,導(dǎo)致div元素在render的時(shí)候會重新生成。那如果key在render前后保持不變呢?例如,將key改為index
問題: 這段代碼在button點(diǎn)擊之后,span的顏色會變嗎?
return (
<div className="App" id="app">
<Spin spinning={spin}></Spin>
{
initMap.map((it,index) => <div key={index}><span>color</span></div>)
}
<button onClick={() => handleClick()}></button>
</div>
);
答案:會
分析: 因?yàn)樵趓ender前后,index不變,所以div不會重新生成,接著對比span元素,span元素在render前后,屬性變化,因此react只會為span元素應(yīng)用新的屬性,但是他們指向的還是之前的元素

第三種情況:key的取值確定且唯一:
在這個(gè)例子中,通過將key設(shè)置成index,span的顏色有了變化,但是在使用key時(shí),React官網(wǎng)不推薦使用index

改造一下上面的代碼
const [initMap, setInitMap] = useState([1,2,3,4]);
const handleClick = () => {
setInitMap([3,2,1,4])
}
return (
<div className="App" id="app">
{
initMap.map((it,index) => <div key={index}><input type="radio" />{it}</div>)
}
<button onClick={() => handleClick()}>點(diǎn)擊</button>
</div>
);
}
在初始化的時(shí)候選中值為3的按鈕

點(diǎn)擊按鈕

我們預(yù)期的效果是,選中的依舊是值為3的按鈕,但此時(shí)變成了值為1的按鈕
分析:
- setState之后會導(dǎo)致render
- div的index不變,所以div不會重新生成,input不受state和props控制,因此元素的狀態(tài)不變
- 所以變化的只有受state影響的it
如果想要達(dá)到預(yù)期效果,我們要設(shè)置唯一且確定的key
測試一:
{
initMap.map((it) => <div key={it}><input type="radio" />{it}</div>)
}
初始化的時(shí)候選中第三個(gè)按鈕

點(diǎn)擊按鈕

這才是符合預(yù)期的效果
思考一下,將key設(shè)置為Math.random(),會有什么效果?按鈕的狀態(tài)會保留嗎?
點(diǎn)擊前:

點(diǎn)擊后:

radio的狀態(tài)不會被保留
通過上面的例子,我們大概可以理解React中key的作用了,下面的內(nèi)容是對React知識點(diǎn)的一些擴(kuò)展
擴(kuò)展內(nèi)容: 文章開始的代碼還涉及React兩個(gè)其他知識點(diǎn),一個(gè)是提到過的React渲染條件,一個(gè)是對真實(shí)dom的操作;
擴(kuò)展一: React渲染條件
import './App.css';
import React, { useState } from 'react';
function App() {
const [initMap, setInitMap] = useState([1,2,3,4]);
const [spin, setSpin] = useState(false);
const handleClick = () => {
setSpin(true); //變化部分
var spanEle = document.getElementsByTagName('span');
Array.from(spanEle).map(it => it.style.color = 'red')
setSpin(false); //變化部分
}
return (
<div className="App" id="app">
<Spin spinning={spin}></Spin>
{
initMap.map((it,index) => <div key={Math.random()}><span>{it}</span></div>)
}
<button onClick={() => handleClick()}></button>
</div>
);
}
export default App;
測試結(jié)果如下 點(diǎn)擊前:

點(diǎn)擊后:

在這段代碼中,div的key仍然使用的是Math.random(),但initMap的state并沒有改變,所以沒有重新渲染,此時(shí)div不會銷毀重建
擴(kuò)展二:是否可以對真實(shí)dom操作
在React中,虛擬dom的出現(xiàn)是為了減少對真實(shí)dom的操作,因?yàn)檎鎸?shí)的dom元素是一個(gè)較復(fù)雜的對象,操作的話計(jì)算量比較大。我們上面的代碼中,都是直接操作dom節(jié)點(diǎn),更改樣式,這樣并不可取。由于React是根據(jù)state和props的變化來渲染頁面,因此通過state來控制頁面渲染比較好
修改后的代碼如下:
function App() {
const [initMap, setInitMap] = useState([1,2,3,4]);
const [spin, setSpin] = useState(false);
const [showColor, setShowColor] = useState(false);
const handleClick = () => {
setInitMap([3,2,1,4]);
setShowColor(true);
}
return (
<div className="App" id="app">
<Spin spinning={spin}>
{
initMap.map((it,index) => <div key={Math.random()}><span className={showColor && 'span-color'}>color</span></div>)
}
</Spin>
<button onClick={() => handleClick()}>點(diǎn)擊</button>
</div>
);
}
此時(shí)span是受控組件,可以通過showColor的狀態(tài)控制元素的渲染
點(diǎn)擊前:

點(diǎn)擊后:

使用state控制渲染后,代碼量會變少,同時(shí)結(jié)果符合預(yù)期
總結(jié)
- 在使用key時(shí),要保證key的唯一和確定性,如果key的值為Math.random(),可能造成組件重新構(gòu)建,使之前對元素的操作失效
- 在渲染頁面時(shí),盡量不要操作真實(shí)的dom,使用state來更新頁面
以上就是詳解React中key的作用的詳細(xì)內(nèi)容,更多關(guān)于React key的作用的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
ReactNative支付密碼輸入框?qū)崿F(xiàn)詳解
這篇文章主要為大家介紹了ReactNative支付密碼輸入框?qū)崿F(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
react+antd4實(shí)現(xiàn)優(yōu)化大批量接口請求
這篇文章主要為大家詳細(xì)介紹了如何使用react hooks + antd4實(shí)現(xiàn)大批量接口請求的前端優(yōu)化,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2024-02-02
React?Hooks中?useRef和useImperativeHandle的使用方式
這篇文章主要介紹了React?Hooks中?useRef和useImperativeHandle的使用方式,文中說明了useRef和useCallback一起使用,?可以解決閉包陷阱的問題,本文結(jié)合實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-10-10
在?React?項(xiàng)目中全量使用?Hooks的方法
這篇文章主要介紹了在?React?項(xiàng)目中全量使用?Hooks,使用 Hooks 能為開發(fā)提升不少效率,但并不代表就要拋棄 Class Component,依舊還有很多場景我們還得用到它,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-10-10
詳解React Angular Vue三大前端技術(shù)
當(dāng)前世界中,技術(shù)發(fā)展非常迅速并且變化迅速,開發(fā)者需要更多的開發(fā)工具來解決不同的問題。本文就對于當(dāng)下主流的前端開發(fā)技術(shù)React、Vue、Angular這三個(gè)框架做個(gè)相對詳盡的探究,目的是為了解開這些前端技術(shù)的面紗,看看各自的廬山真面目。2021-05-05
簡談創(chuàng)建React Component的幾種方式
這篇文章主要介紹了創(chuàng)建React Component的幾種方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下2019-06-06

