網站首頁 編程語言 正文
在 React 中我們經常需要渲染列表,比如展示好友列表。
常用寫法是用 Arrary.prototype.map 方法,將數組形式的數據映射為 JSX.Element 數組,并嵌入到組件要返回的 JSX.Element 中,如下:
function FriendList() {
const [items, setItems] = useState(['AB教程網', '小明', '張三']);
return (
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
);
}
你需要給每個項提供 key 屬性作為標識,以區分不同的項。如果你不加 key,React 會警告你:
Warning: Each child in a list should have a unique "key" prop.
為什么需要 key?
在回答這個問題之前,我們先簡單了解一下 React 的 DOM Diff 算法原理。
React 會在狀態發生變化時,對真實 DOM 樹按需批量更新,產生新的 UI。
為此底層做的工作是:將新舊兩棵虛擬 DOM 樹進行 diff 對比,計算出 patch 補丁,打到真實 DOM 樹上。
為了高效,React 的 diff 算法做了限制:
- 只做同層級的節點對比,不跨層級比較。
- 如果元素的類型不同(如從 p 變成 div),那它們就是不相同的,會銷毀整個舊子樹,并調用其下組件的卸載鉤子,然后再創建全新的樹,相當消耗性能。
- 如果類型相同,會進行打補丁操作(如更新 className 和標簽下的文本內容)。
但這樣做會有一個問題,如果同級的多節點??只是位置發生了變化?,但因為相同索引位置對不上,又發現不能復用,就要銷毀一棵樹并創建一棵新樹,實在是太過于低效了。
于是 React 給開發者提供 key 來標記節點,來優化 React diff 算法,告知 React 某個節點其實沒有被移除或不能被原地復用,只是換了位置而已,讓 React 更新一下位置。
列表渲染不提供 key 會怎樣?
不提供 key,React 就無法確定某個節點是否移動了。
React 就只會對比相同位置的兩個節點,如果它們類型相同(比如都是 li 元素),就會對比 props 的不同,進行 props 的打補丁。
?因為?列表渲染通常都是相同的類型,所以位置變動時,多半是會觸發節點原地復用效果,倒是不用擔心樹的銷毀重建發生。
原地復用在不提供 key 的時候有時候也是能正確渲染的。
除了一種情況,就是?這個節點有自己的內部狀態,最經典的莫過于輸入框。
function FriendList() {
const [items, setItems] = useState(['AB教程網', '小明', '張三']);
const swap = () => {
[items[0], items[1]] = [items[1], items[0]];
setItems([...items]);
};
return (
<div>
<ul>
{items.map((item) => (
<li>{item}<input /></li>
))}
</ul>
<button onClick={() => { swap(); }}>
交換
</button>
</div>
);
}
我們給第一和第二個輸入框輸入內容。
再點擊 “交換” 按鈕,交換數組第一和第二個元素位置。
然后我們看到 input 前面的文字正確交換了,但是輸入框里的內容卻沒有交換。
?原因是 React 做了原地復用,而 input 沒有傳 props,不需要打 props 補丁,保持了原樣。
這個問題怎么解決?加 key。讓 React 知道你的節點需要移動,你得這樣寫:?
items.map((item) => (
<li key={item}>{item}<input /></li>
))
不使用 key 的另一個缺點是:因為原地復用會使傳入的 props 發生變化,導致不能利用好 React.memo 的組件緩存能力。
列表渲染的 key 用數組索引會怎樣?
效果和不使用 key 相同,依舊是新舊節點的相同索引位置對比,但是控制臺不會打印警告。
應該用什么值作為 key?
對于節點,你需要用一個唯一的 id 賦值給 key,通常會是數組的 id,比如后端返回的好友列表的好友 id。
const [items, setItems] = useState([
{ id: 5, name: 'AB教程網' },
{ id: 9, name: '小明' },
{ id: 87, name: '張三' },
{ id: 91, name: 'AB教程網' }
]);
const list = items.map((item) => (
<li key={item.id}>{item.name}</li>
));
如果后端沒有返回 id,你可以自己手動用一個 id 生成器補上一個 id,雖然不太優雅就是了。比如:
const items = ['AB教程網', '張三'];
const genId = (() => {
let i = 0;
return () => {
return i++;
}
})();
const itemsWithId = items.map(item => ({ id: genId(), val: item }));
// [{id: 0, val: 'AB教程網'}, {id: 1, val: '張三'}]
對了,這個 key 只需要在同一個層級的節點唯一即可,不要求所有層級的 key 都是唯一的。
另外,如果你確保你的列表渲染后直到被銷毀,不會有位置上的變化,可以使用數組索引為 key。
結尾
對于列表的渲染,我們有必要提供 key,來對節點進行區分,React 的 DOM Diff 算法會基于 key 進行節點位置的調整,確保一些涉及到內部狀態的節點的渲染狀態。
通常來說,key 值應該是唯一的,通常來自后端返回的數據。在你確認列表不會發生位置變更時,可以使用數組索引作為 key,以去掉惱人的警告提示。
有一個點需要說明的是,key 并不是列表渲染的專屬,普通的節點也可以用 key。
原文鏈接:https://developer.51cto.com/article/712896.html
相關推薦
- 2022-11-12 Python?Multinomial?Naive?Bayes多項貝葉斯模型實現原理介紹_python
- 2023-03-22 gin正確多次讀取http?request?body內容實現詳解_Golang
- 2023-08-01 v-model 和 .sync 深度解讀
- 2022-12-24 Android開發中Signal背后的bug與解決_Android
- 2022-09-21 Shell自動化配置SSH免密登錄和取消SSH免密配置腳本_linux shell
- 2022-05-13 Scrapy-middlewares對象
- 2022-07-21 element ui 中el-row的gutter失效
- 2022-02-15 H5移動端大轉盤抽獎插件, 簡單、易用、無依賴
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支