要了解react中key的作用,可以從key的取值入手,key的取值可以分為三種,不定值、索引值、確定且唯一值
在下面的代碼中,key的取值是不定值(math.random())
問題: 點擊按鈕的時候,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;
答案是:不會
這個問題涉及react渲染機制和diff算法
官網中對于diff有如下規則:
- 當元素類型變化時,會銷毀重建
- 當元素類型不變時,對比屬性
- 當組件元素類型不變時,通過props遞歸判斷子節點
- 遞歸對比子節點,當子節點是列表時,通過key和props來判斷。若key一致,則進行更新,若key不一致,就銷毀重建
分析上述問題:
當點擊按鈕時,setinitmap([1,2,3,4])會造成渲染,渲染時會生成新的虛擬dom,但此時獲取到的span元素是之前的元素(因為setinitmap是異步執行的),所以新舊dom會做對比
在initmap.map((it,index) => <div key={math.random()}><span>color</span></div>)這段代碼中
這里的div是列表,對比第四條diff規則,react會根據key來判斷是否更新真實dom。key= {math.random()},新舊dom的值不一致,就會重新生成div。而我們是給更新之前的元素加了紅色的樣式,所以重新創建的元素上不會有這個樣式,效果如下
第二種情況:key的取值為索引值
上面我們分析的結果是,因為key的變化,導致div元素在render的時候會重新生成。那如果key在render前后保持不變呢?例如,將key改為index
問題: 這段代碼在button點擊之后,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>
- );
答案:會
分析: 因為在render前后,index不變,所以div不會重新生成,接著對比span元素,span元素在render前后,屬性變化,因此react只會為span元素應用新的屬性,但是他們指向的還是之前的元素
第三種情況:key的取值確定且唯一:
在這個例子中,通過將key設置成index,span的顏色有了變化,但是在使用key時,react官網不推薦使用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()}>點擊</button>
- </div>
- );
- }
在初始化的時候選中值為3的按鈕
點擊按鈕
我們預期的效果是,選中的依舊是值為3的按鈕,但此時變成了值為1的按鈕
分析:
- setstate之后會導致render
- div的index不變,所以div不會重新生成,input不受state和props控制,因此元素的狀態不變
- 所以變化的只有受state影響的it
如果想要達到預期效果,我們要設置唯一且確定的key
測試一:
- {
- initMap.map((it) => <div key={it}><input type="radio" />{it}</div>)
- }
初始化的時候選中第三個按鈕
點擊按鈕
這才是符合預期的效果
思考一下,將key設置為math.random(),會有什么效果?按鈕的狀態會保留嗎?
點擊前:
點擊后:
radio的狀態不會被保留
通過上面的例子,我們大概可以理解react中key的作用了,下面的內容是對react知識點的一些擴展
擴展內容: 文章開始的代碼還涉及react兩個其他知識點,一個是提到過的react渲染條件,一個是對真實dom的操作;
擴展一: 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;
測試結果如下 點擊前:
點擊后:
在這段代碼中,div的key仍然使用的是math.random(),但initmap的state并沒有改變,所以沒有重新渲染,此時div不會銷毀重建
擴展二:是否可以對真實dom操作
在react中,虛擬dom的出現是為了減少對真實dom的操作,因為真實的dom元素是一個較復雜的對象,操作的話計算量比較大。我們上面的代碼中,都是直接操作dom節點,更改樣式,這樣并不可取。由于react是根據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()}>點擊</button>
- </div>
- );
- }
此時span是受控組件,可以通過showcolor的狀態控制元素的渲染
點擊前:
點擊后:
使用state控制渲染后,代碼量會變少,同時結果符合預期
總結
- 在使用key時,要保證key的唯一和確定性,如果key的值為math.random(),可能造成組件重新構建,使之前對元素的操作失效
- 在渲染頁面時,盡量不要操作真實的dom,使用state來更新頁面
以上就是詳解react中key的作用的詳細內容,更多關于react key的作用的資料請關注服務器之家其它相關文章!
原文鏈接:https://juejin.cn/post/6949420944901275655