逃生艙口
您的某些元件可能需要控制並與 React 外部的系統同步。例如,您可能需要使用瀏覽器 API 來聚焦輸入框、播放或暫停一個非 React 實作的影片播放器,或是連線並監聽遠端伺服器的訊息。在本章中,您將學習能讓您「跨出」React 並連線到外部系統的逃生艙口。您應用程式的大部分邏輯與資料流不應依賴這些功能。
使用 Ref 參照值
當您希望元件「記住」某些資訊,但又不希望該資訊觸發新的渲染時,您可以使用ref:
與狀態類似,ref 會在重新渲染之間由 React 保留。然而,設定狀態會重新渲染元件,而變更 ref 則不會!您可以透過ref.current屬性存取該 ref 的目前值。
ref 就像是元件的一個秘密口袋,React 不會追蹤它。例如,您可以使用 ref 來儲存計時器 ID、DOM 元素以及其他不影響元件渲染輸出的物件。
使用 Ref 操作 DOM
React 會自動更新 DOM 以符合您的渲染輸出,因此您的元件通常不需要操作它。然而,有時您可能需要存取由 React 管理的 DOM 元素——例如,聚焦某個節點、捲動到該節點,或測量其大小和位置。React 中沒有內建的方法來執行這些操作,因此您將需要一個指向該 DOM 節點的 ref。例如,點擊按鈕將使用 ref 來聚焦輸入框:
使用 Effect 進行同步
有些元件需要與外部系統同步。例如,您可能希望根據 React 狀態來控制一個非 React 元件、建立伺服器連線,或在元件出現在畫面上時傳送分析日誌。與讓您處理特定事件的事件處理器Effect不同,讓您在渲染後執行一些程式碼。使用它們來將您的元件與 React 外部的系統同步。
按幾次播放/暫停,看看影片播放器如何與isPlaying屬性值保持同步:
許多 Effect 也會在執行後進行「清理」。例如,一個設定聊天伺服器連線的 Effect 應該回傳一個清理函式,告訴 React 如何將你的元件從該伺服器斷開連線:
在開發環境中,React 會立即額外執行並清理你的 Effect 一次。這就是為什麼你會看到"✅ Connecting..."被印出兩次。這確保你不會忘記實作清理函式。
你可能不需要 Effect
Effect 是 React 範式的一個逃生艙。它們讓你「走出」React,並將你的元件與某些外部系統同步。如果沒有涉及外部系統(例如,當某些 props 或狀態改變時,你只想更新元件的狀態),你就不應該需要 Effect。移除不必要的 Effect 將使你的程式碼更容易理解、執行更快,且更不容易出錯。
有兩種常見情況你不需要 Effect:
- 你不需要 Effect 來轉換資料以供渲染。
- 你不需要 Effect 來處理使用者事件。
例如,你不需要一個 Effect 來根據其他狀態調整某些狀態:
相反地,盡可能在渲染期間計算:
然而,你確實需要 Effect 來與外部系統同步。
響應式 Effect 的生命週期
Effect 的生命週期與元件不同。元件可能會掛載、更新或卸載。一個 Effect 只能做兩件事:開始同步某些東西,然後稍後停止同步它。如果你的 Effect 依賴於隨時間變化的 props 和狀態,這個循環可能會發生多次。
這個 Effect 依賴於roomIdprop 的值。Props 是響應式值,這意味著它們可以在重新渲染時改變。請注意,如果重新同步(並重新連線到伺服器):roomId改變,Effect 會
React 提供了一個 linter 規則來檢查你是否正確指定了 Effect 的依賴項。如果你忘記在上述範例的依賴項列表中指定roomId,linter 會自動發現這個錯誤。
將事件與 Effect 分離
事件處理函式僅在您再次執行相同互動時才會重新執行。與事件處理函式不同,如果 Effect 讀取的任何值(例如 props 或 state)與上次渲染時不同,Effect 會重新同步。有時,您會希望混合這兩種行為:一個 Effect 會因某些值而重新執行,但不會因其他值而重新執行。
Effect 內的所有程式碼都是響應式的。 如果它讀取的某些響應式值因重新渲染而發生變化,它將再次執行。例如,如果 roomId 或 theme發生變化,這個 Effect 將重新連接到聊天室:
這並不理想。您希望僅在 roomId發生變化時才重新連接到聊天室。切換theme 不應該重新連接到聊天室!將讀取 theme的程式碼從您的 Effect 中移出,放入一個Effect Event中:
Effect Event 內的程式碼不是響應式的,因此更改theme不再使您的 Effect 重新連接。
移除 Effect 依賴項
當您編寫一個 Effect 時,linter 會驗證您是否已將 Effect 讀取的每個響應式值(例如 props 和 state)包含在 Effect 的依賴項清單中。這確保您的 Effect 與元件的最新 props 和 state 保持同步。不必要的依賴項可能導致您的 Effect 執行過於頻繁,甚至產生無限迴圈。移除它們的方式取決於具體情況。
例如,這個 Effect 依賴於options物件,該物件在您每次編輯輸入時都會重新建立:
您不會希望每次在聊天室中開始輸入訊息時,聊天都重新連線。為了解決這個問題,請將 options物件的建立移到 Effect 內部,這樣 Effect 就只依賴於roomId字串:
請注意,您並不是一開始就編輯依賴陣列來移除 options依賴。那樣做是錯誤的。相反地,您修改了周圍的程式碼,使得該依賴變得不必要。請將依賴陣列視為您的 Effect 程式碼所使用的所有響應式值的清單。您並非刻意選擇要將什麼放入該清單。清單描述的是您的程式碼。要改變依賴陣列,請先改變程式碼。
使用自訂 Hook 重複使用邏輯
React 內建了像 useState、useContext 和 useEffect這樣的 Hook。有時,您可能會希望有針對更特定用途的 Hook:例如,用於獲取資料、追蹤使用者是否在線,或連接到聊天室。為此,您可以為應用程式的需求建立自己的 Hook。
在此範例中,自訂 HookusePointerPosition追蹤游標位置,而自訂 HookuseDelayedValue 則傳回一個「延遲」於您傳入的值特定毫秒數的值。將游標移到沙箱預覽區域上方,即可看到跟隨游標移動的一串點:
您可以建立自訂 Hook,將它們組合在一起,在它們之間傳遞資料,並在元件之間重複使用它們。隨著應用程式的成長,您手動編寫的 Effect 將會減少,因為您將能夠重複使用已經編寫好的自訂 Hook。React 社群也維護了許多優秀的自訂 Hook。
接下來呢?
前往 使用 Refs 引用值 開始逐頁閱讀本章!
