天天看點

React元件設計:重新認識受控與非受控元件

react 官網中對非受控元件與受控元件作了如圖中下劃線的邊界定義。一經推敲, 該定義是缺乏了些完整性和嚴謹性的, 比如針對非表單元件(彈框、輪播圖)如何劃分受控與非受控的邊界?又比如非受控元件是否真的如文案上所說的資料的展示與變更都由 dom 自身接管呢?

在非受控元件中, 通常業務調用方隻需傳入一個初始預設值便可使用該元件。以 Input 元件為例:

在受控元件中, 數值的展示與變更則分别由元件的 state 與 setState 接管。同樣以 Input 元件為例:

有意思的一個問題來了, Input 元件到底是受控的還是非受控的? 我們甚至還可以對代碼稍加改動成 <Input defaultValue={1} /> 的最初調用方式:

盡管此時 Input 元件本身是一個受控元件, 但與之相對的調用方失去了更改 Input 元件值的控制權, 是以對調用方而言, Input 元件是一個非受控元件。值得一提的是, 以非受控元件的使用方式去調用受控元件是一種反模式, 在下文中會分析其中的弊端。

如何做到不管對于元件提供方還是調用方 Input 元件都為受控元件呢? 提供方讓出控制權即可, 調整代碼如下codesandbox:

經過上述代碼的推演後, 概括如下: 受控以及非受控元件的邊界劃分取決于目前元件對于子元件值的變更是否擁有控制權。如若有則該子元件是目前元件的受控元件; 如若沒有則該子元件是目前元件的非受控元件。

基于調用方對于受控元件擁有控制權這一認知, 是以受控元件相較非受控元件能賦予調用方更多的定制化職能。這一思路與軟體開發中的開放/封閉原則有異曲同工之妙, 同時讓筆者受益匪淺的 Inversion of Control 也是類似的思想。

借助受控元件的賦能, 以 Input 元件為例, 比如調用方可以更為自由地對值進行校驗限制, 又比如在值發生變更時執行一些額外邏輯。

是以綜合基礎元件擴充性與通用性的考慮, 受控元件的職能相較非受控元件更加寬泛, 建議優先使用受控元件來建構基礎元件。

首先何謂反模式? 筆者将其總結為增大隐性 bug 出現機率的模式, 該模式是最佳實踐的對立經驗。如若使用了反模式就不得不花更多的精力去避免潛在 bug。官網對反模式也有很好的概括總結。

緣何上文提到以非受控元件的使用方式去調用受控元件是一種反模式? 觀察 Input 元件的第一行代碼, 其将 defaultValue 指派給 value, 這種将 props 指派給 state 的指派行為在一定程度上會增加某些隐性 bug 的出現機率。

比如在切換導航欄的場景中, 恰巧兩個導航中傳進元件的 defaultValue 是相同的值, 在導航切換的過程中便會将導航一中的 Input 的狀态值帶到導航二中, 這顯然會讓使用方感到困惑。codesandbox

如何避免使用該反模式同時有效解決問題呢? 官方提供了兩種較為優質的解法, 将其留給大家作為思考。

方法一: 使用完全受控元件(更為推薦)

方法二: 使用完全非受控元件 + key