如何使用 React 鍵避免組件衝突
React 框架可能會帶來複雜的挑戰,導致意外行為或難以捉摸的缺陷,對於那些缺乏對其根本原因的理解的人來說,這些缺陷很難解決。
當根據特定屬性值有條件地渲染組件時,可能會出現稱為“同一組件的條件渲染”錯誤的異常情況。要深入研究此問題並使用 React 鍵找到其解決方案,請仔細檢查該問題。
React 組件並不總是獨立的
React 的簡單結構是強烈建議熟練掌握這個 JavaScript 庫來構建用戶界面的一個重要原因。然而,儘管使用 React 有很多好處,但重要的是要承認它確實存在一些缺陷和缺陷。
所討論的問題是由於組件根據特定標準偶然顯示的情況而產生的,但在其實例化過程中卻提供了不同的屬性。
React 利用其虛擬 DOM 比較算法來有效地僅更新自上次渲染以來實際 DOM 中發生更改的部分。該策略基於以下假設:大多數更改發生在層次結構內的各個元素級別,而不是頂層。因此,當比較具有相同標記且沒有本地狀態的同級組件時,React 的算法會將它們識別為等效的並跳過它們的重新渲染。因此,這些組件之一中定義的任何狀態在連續渲染中都會持續存在,因為它們不會刷新。
讓我對該聲明進行更完善的解釋。假設我們檢查以下組件列表的一個實例:
import { useState, useEffect } from "react"
export function Counter({name}) {
const [count, setCount] = useState(0)
return(
<div>
<div>{name}</div>
<button onClick={() => setCount(c => c-1)}> - </button>
<br />
<button onClick={() => setCount(c => c \+ 1)}> \+ </button>
</div>
)
}
“Counter”組件通過對象解構行為從其父實體接收命名屬性,該屬性用作在 React 應用程序上下文中合併屬性的方法。隨後,該組件會在 HTML div 元素中顯示此指定。此外,它還提供了兩個界面元素-一個旨在減少狀態中存儲的值,另一個用於增加狀態中存儲的值。
上述代碼,特別是與“App”組件內的應用程序實現相關的代碼,因其潛在問題而受到質疑。應該注意的是,所提供的代碼片段中不存在任何故障;相反,它在於使用所述“App”組件中的“counter”變量。
import { useState } from "react"
import { Counter } from "./Counter"
export default function App() {
const [isKingsley, setIsKingsley] = useState(true)
return(
<div>
{ isKingsley ? <Counter name="Kingsley"/> : <Counter name="Sally"/> }
<br/>
<button onClick={() => setIsKingsley(k => !k)}> Swap </button>
</div>
)
}
上述代碼默認顯示名為“Kingsley”的枚舉。然而,如果將所述計數器的計數增加到值“五”並隨後點擊“交換”列表設備,它將立即顯示名為“Sally”的替代枚舉。
然而,挑戰在於計數器在重新排列後不會返回到其初始狀態零。
由於兩個組件實例以特定順序呈現相同的元素集,因此出現了當前的問題。然而,React 無法區分“Kingsley”和“Sally”計數器,因為它們具有共享屬性,即“name”屬性。遺憾的是,React 沒有利用這種區別來識別具有相同屬性值的組件之間的差異。
針對兩個計數器彼此重疊顯示的問題的一種可能的解決方案是修改文檔對像模型(DOM)的結構,以便為兩個計數器創建不同的樹結構。具體來說,可以將第一計數器包含在 div 元素內,將第二計數器包含在section 元素內。通過這樣做,兩個元素將被單獨渲染,避免重疊。然而,實現這種方法需要對 DOM 及其屬性有透徹的了解。
import { useState } from "react"
import { Counter } from "./Counter"
export default function App() {
const [isKingsley, setIsKingsley] = useState(true)
return (
<div>
{ isKingsley ?
(<div>
<Counter name="Kingsley"/>
</div>)
:
(<section>
<Counter name="Sally"/>
</section>)
}
<br />
<button onClick={() => setIsKingsley(k => !k)}> Swap </button>
</div>
)
}
當單擊“Kingsley”按鈕並與 DOM 樹中的另一個元素交換時,應用程序的行為會發生變化。由於原始 DOM 樹和修改後的 DOM 樹的結構之間的差異,這會導致狀態重置。
“ ”,其中最裡麵包含單個元素。相反,當“isKingsley”值在 false 和 true 之間翻轉時,生成的結構會經歷從“ ”到“ ”的更改,從而用包含原始組件及其子組件的最外層標籤替換最內層標籤。這種結構轉換促使 React 通過以下方式重新創建 Counter 組件
或者,可以在不修改 HTML 代碼的底層結構組件的情況下解決此問題。這種方法無需調整格式即可有效解決當前問題。
使用按鍵渲染新組件
為了讓 React 在渲染過程中區分組件,它依賴於鍵。因此,當呈現相同的組件時,有必要為每個實例分配一個不同的鍵,以告知 React 它們的個性。
通過包含附加鍵來修改每個計數器,如下所示:
import { useState } from "react"
import { Counter } from "./Counter"
export default function App() {
const [isKingsley, setIsKingsley] = useState(true)
return(
<div>
{ isKingsley ?
<Counter key="Kingsley" name="Kingsley"/> :
<Counter key="Sally" name="Sally"/>
}
<br/>
<button onClick={() => setIsKingsley(k => !k)}> Swap </button>
</div>
)
}
增加“Kingsley”計數器的值並單擊“Swap”按鈕後,React 會生成一個新的計數器實例,並將其內部狀態恢復到初始值零。
在 React 中渲染包含統一數據類型的多個實例的數組時,強烈建議使用唯一鍵來區分它們。這是因為 React 缺乏區分此類數組中各個元素的能力,並且通過鍵賦予不同的標識符可確保框架唯一地識別每個實例。
export default function App() {
const names = ["Kingsley", "John", "Ahmed"]
return(
<div>
{ names.map((name, index) => {
return <Counter key={index} name={name} />
})}
</div>
)
}
React 對數組中分配有鍵的每個單獨元素使用稱為“計數器”的內部數據結構。這使得系統能夠實時檢測和更新對關聯陣列所做的任何修改。
另一個高級關鍵用例
您可以使用鍵根據狀態變量的值在兩個不同元素之間建立關係。換句話說,您可以將輸入組件與根據狀態變量的條件而變化的各種組件連接。
為了進行演示,請調整 App 組件:
import { useState } from "react"
export default function App() {
const [isKingsley, setIsKingsley] = useState(true)
return(
<div>
{ isKingsley ? <div>Kingsley's Score</div> : <div>Sally's score</div> }
<input key={ isKingsley? "Kingsley" : "Sally" } type="number"/>
<br/>
<button onClick={() => setIsKingsley(k => !k)}> Swap </button>
</div>
)
}
每當用戶通過單擊按鈕在與 Kingsley 和 Sally 關聯的 div 元素之間交換時,輸入的鍵屬性就會自動從“Kingsley”修改為“Sally”,反之亦然。因此,React 需要在每次連續按下按鈕時完全重建輸入元素。
優化 React 應用程序的更多技巧
優化代碼對於在 Web 和移動應用程序中提供愉快的用戶體驗至關重要,因為它在確保應用程序平穩高效運行方面發揮著關鍵作用。通過熟悉各種優化技術,您可以最大限度地提高基於 React 的應用程序的性能並提高整體用戶滿意度。
React Native 應用程序與 Web 應用程序一樣,也受益於各種優化策略。可以應用多種性能增強技術來改進其功能和用戶體驗。