v19.2Latest

保持元件純粹

有些 JavaScript 函式是純粹的。純粹函式僅執行計算,不做其他事情。透過嚴格地僅將您的元件編寫為純粹函式,您可以避免隨著程式碼庫增長而出現的一整類令人困惑的錯誤和不可預測的行為。然而,要獲得這些好處,您必須遵循一些規則。

您將學習
  • 什麼是純粹性以及它如何幫助您避免錯誤
  • 如何透過在渲染階段避免變更來保持元件純粹
  • 如何使用嚴格模式來找出元件中的錯誤

純粹性:元件如同公式

在電腦科學(尤其是函數式程式設計領域)中,純粹函式是具有以下特徵的函式:

  • 它只管自己的事。它不會改變在它被呼叫之前就已存在的任何物件或變數。
  • 相同輸入,相同輸出。給定相同的輸入,純粹函式應始終返回相同的結果。

您可能已經熟悉純粹函式的一個例子:數學中的公式。

考慮這個數學公式:y= 2x

如果x= 2,那麼y= 4。總是如此。

如果x= 3,那麼y= 6。總是如此。

如果x= 3y不會有時是9–12.5,取決於一天中的時間或股市的狀態。

如果y= 2xx= 3,那麼y總是等於6

如果我們將其轉換為 JavaScript 函式,它看起來會像這樣:

在上面的例子中,double是一個純粹函式。如果您傳入3,它將返回6。總是如此。

React 是圍繞這個概念設計的。React 假設您編寫的每個元件都是一個純粹函式。這意味著您編寫的 React 元件在給定相同輸入時必須始終返回相同的 JSX:

當您將drinkers={2}傳遞給Recipe時,它將返回包含2 cups of water的 JSX。總是如此。

如果您傳遞drinkers={4},它將返回包含4 cups of water的 JSX。總是如此。

就像數學公式一樣。

您可以將您的元件視為食譜:如果您遵循它們並且在烹飪過程中不引入新的食材,您每次都會得到相同的菜餚。那個「菜餚」就是元件提供給 React 進行渲染的 JSX。

x 人份的茶食譜:取 x 杯水,加入 x 匙茶葉和 0.5x 匙香料,以及 0.5x 杯牛奶

插圖由Rachel Lee Nabors繪製

副作用:(非)預期的後果

React 的渲染過程必須始終是純粹的。元件應該只返回它們的 JSX,而不應改變渲染之前就已存在的任何物件或變數——否則會使它們變得不純粹!

以下是一個違反此規則的元件:

這個元件正在讀取和寫入一個在其外部宣告的guest 變數。這意味著 多次呼叫此元件將會產生不同的 JSX! 更甚者,如果 其他 元件讀取 guest,它們也會產生不同的 JSX,取決於它們何時被渲染!這是不可預測的。

回到我們的公式 y= 2x,現在即使x= 2,我們也無法相信y= 4。我們的測試可能會失敗,使用者會感到困惑,飛機可能會從天上掉下來——你可以看到這會導致多麼令人困惑的錯誤!

你可以透過 將 guest 作為 prop 傳遞來修正此元件

現在你的元件是純粹的,因為它返回的 JSX 僅取決於guestprop。

一般來說,你不應該期望你的元件以任何特定順序渲染。無論你在 y= 2x之前或之後呼叫y= 5x都無關緊要:兩個公式將彼此獨立地解析。同樣地,每個元件應該只「為自己思考」,而不應嘗試在渲染期間與其他元件協調或依賴它們。渲染就像學校考試:每個元件都應該獨立計算 JSX!

Deep Dive
使用嚴格模式檢測不純粹的計算

局部突變:你的元件的小秘密

在上述範例中,問題在於元件在渲染時更改了一個既有的 變數。這通常被稱為 「突變」,以使其聽起來有點嚇人。純粹函式不會突變函式作用域外的變數或在呼叫前建立的物件——這會使它們變得不純粹!

然而,更改你在渲染期間 剛剛建立的變數和物件是完全沒問題的。在這個範例中,你建立了一個[]陣列,將其賦值給一個cups 變數,然後 push一打杯子進去:

如果 cups 變數或 [] 陣列是在 TeaGathering函式外部建立的,這將是一個巨大的問題!你將透過將項目推入該陣列來更改一個既有的 物件。

然而,這沒問題,因為你是在 同一次渲染期間,在 TeaGathering內部建立它們的。TeaGathering外部的任何程式碼都不會知道這件事。這被稱為「局部突變」——它就像是你的元件的小秘密。

可以引發副作用的地方

雖然函數式程式設計非常依賴純粹性,但在某個時候,某個地方,某些東西 必須改變。這正是程式設計的意義所在!這些改變——更新畫面、開始動畫、更改資料——被稱為 副作用。 它們是發生在 「一旁」的事情,而不是在渲染期間。

在 React 中,副作用通常屬於 事件處理器。事件處理器是當你執行某些操作時 React 執行的函式——例如,當你點擊按鈕時。儘管事件處理器定義你的元件內部,但它們不會渲染期間執行!因此事件處理器不需要是純粹的。

如果你已用盡所有其他選項,並且找不到適合你副作用的事件處理器,你仍然可以使用元件中的 useEffect呼叫將其附加到返回的 JSX 上。這告訴 React 稍後在渲染之後、允許副作用時執行它。然而,這種方法應該是你最後的手段。

在可能的情況下,嘗試僅用渲染來表達你的邏輯。你會驚訝於這能帶你走多遠!

Deep Dive
為什麼 React 重視純粹性?

總結

  • 元件必須是純粹的,這意味著:
    • 它只管自己的事。它不應該改變渲染前就已存在的任何物件或變數。
    • 相同的輸入,相同的輸出。給定相同的輸入,元件應該總是回傳相同的 JSX。
  • 渲染可能隨時發生,因此元件不應該依賴彼此的渲染順序。
  • 您不應該變異元件用於渲染的任何輸入。這包括 props、state 和 context。要更新畫面,請「設定」狀態而不是變異既有的物件。
  • 努力在您回傳的 JSX 中表達元件的邏輯。當您需要「改變事物」時,通常會希望在事件處理程式中進行。作為最後的手段,您可以使用useEffect
  • 撰寫純粹函式需要一些練習,但它釋放了 React 典範的力量。

嘗試一些挑戰

Challenge 1 of 3:修復壞掉的時鐘 #

這個元件試圖在午夜到早上六點之間將 <h1> 的 CSS 類別設定為 "night",並在其他時間設定為 "day"。然而,它無法正常運作。你能修復這個元件嗎?

你可以透過暫時更改電腦的時區來驗證你的解決方案是否有效。當目前時間介於午夜到早上六點之間時,時鐘應該會有反轉的顏色!