React 禁止直接使用 useEffect
直接使用 useEffect 是一個與外部同步的逃生艙。它應存在於一個經過審查、特定於領域(domain-specific)的 hook 中,用以連接真實的外部系統;元件應保持無 effect 狀態。
範圍
取代 effect 會改變相關的行為。全庫的 lint 強制執行是一個獨立的任務:僅在要求或現有倉庫政策要求時才應用它。審查請求會產生發現結果;實作請求則授權範圍內的取代與驗證,無需再次通過核准檢查點。
工作流程
-
找出所觸及範圍內的直接匯入 (
import { useEffect } from 'react')、命名空間呼叫 (React.useEffect(...)) 以及useEffect的包裝函式 (wrappers)。 -
將每個 effect 分類,並按以下偏好順序取代:
- 渲染時計算 (render-time calculation)
- 伺服器、載入器或框架資料 API (server, loader, or framework data API)
- 倉庫的伺服器狀態層(例如,一個帶有變數鍵和傳遞
signal參數的 TanStack QueryqueryOptions工廠) - 事件處理器、動作或突變 (event handler, action, or mutation)
- 以 key 為基礎的元件邊界 (keyed component boundary)
useSyncExternalStore- 經過審查、用於真實外部系統的特定領域 hook
如果上述選項均不適用,請在保留 effect 之前說明相關的外部同步契約。 取代方式 涵蓋了非顯而易見的情況。
-
保留倉庫的框架、資料層、lint 配置與相依性政策。僅在請求或倉庫政策授權時才添加函式庫;若沒有伺服器狀態層,則將剩餘的 effect 視為設計缺口。
-
執行 lint、型別與測試閘門,以及針對變更行為的最小真實 UI 或 hook 檢查。
合法的 Effect
每個合法的 effect 都應放在以其外部系統命名的 hook 中(例如一個聊天連線、地圖 SDK、小工具),且位於自己的檔案中。該 hook 負責設定 (setup) 與清理 (cleanup),將反應式輸入 (reactive inputs) 作為參數,並列出設定或清理所讀取的每個值,以便 react-hooks/exhaustive-deps 在不被抑制 (unsuppressed) 的情況下通過。優先使用倉庫中已存在的 hook。
該 hook 專用於一個外部系統。一個接受回呼函式和呼叫者提供的相依性陣列的通用包裝函式(例如 useUpdateEffect(fn, deps)、useMountEffect(fn)),只是換了個名字的直接 effect。伺服器狀態、prop 副本、使用者動作和基於 key 的重置,各自在上述取代選項中有對應的解決方案。
強制執行
當強制執行在範圍內時,擴展現有倉庫的 linter,使其同時拒絕具名的 useEffect 匯入 (named imports) 以及 React.useEffect(...) 呼叫。僅允許具名、經過審查的整合 hook 檔案(而不是諸如 src/hooks/** 的目錄);將通用包裝函式及其呼叫者報告為違規,需要遷移。在規範的本地閘門 (canonical local gate) 與 CI 中執行相同的 lint 範圍,並使用固定版本的工具。
在審查中,每個新的直接 effect 都是一個發現結果,除非那次變更文件化並證明了一個合法的外部系統邊界。要求提供取代方案,而不是調整相依性陣列。
邊界
- 將遷移範圍限定在所觸及或請求的程式碼表面。
- 除非被要求,否則保持
useLayoutEffect、useInsertionEffect、框架生命週期 API 以及非 React effect 系統不變,包括在 lint 規則中。 - 僅針對衡量後的行為或倉庫既有的模式,才應用效能最佳化原語 (performance primitives)。
- 上游歸屬: upstream。