Community程式設計與開發github.com

uinaf/agent-skills

審查或取代 React useEffect 呼叫,或強制執行無直接 effect 政策。適用於 effect 實作、重構或 lint 政策;不包括無關的 React 工作與非 React effect 系統。

agent-skills 是什麼?

此技能旨在協助開發者或 AI 代理在 React 程式碼庫中管理 `useEffect`。其核心宗旨是將直接使用 `useEffect` 視為一種應受控管的逃生艙,鼓勵開發者將 effect 邏輯封裝在特定於外部系統的自訂 hook 中,並優先使用框架、資料層或事件處理器等替代方案。 技能支援多種工作流程,包括審查現有效果、實作取代方案,以及透過 lint 規則進行全庫強制執行。它會根據效果類別提供一套明確的取代順序(例如,優先採用渲染時計算、伺服器資料 API 或事件處理器),並要求所有合法 effect 必須放在以外部系統命名的專屬 hook 檔案中,且需滿足 `react-hooks/exhaustive-deps` 規則。 此技能適用於 Claude Code、Cursor 或 Codex 等 AI 代理工具,可顯著減少應用程式中因不當使用 `useEffect` 所導致的 bug 與非預期行為。它不處理 `useLayoutEffect`、`useInsertionEffect` 或非 React 的 effect 系統。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/uinaf/agent-skills/tree/HEAD/skills/react-ban-use-effect

Installed? Explore more 程式設計與開發 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

React 禁止直接使用 useEffect

直接使用 useEffect 是一個與外部同步的逃生艙。它應存在於一個經過審查、特定於領域(domain-specific)的 hook 中,用以連接真實的外部系統;元件應保持無 effect 狀態。

範圍

取代 effect 會改變相關的行為。全庫的 lint 強制執行是一個獨立的任務:僅在要求或現有倉庫政策要求時才應用它。審查請求會產生發現結果;實作請求則授權範圍內的取代與驗證,無需再次通過核准檢查點。

工作流程

  1. 找出所觸及範圍內的直接匯入 (import { useEffect } from 'react')、命名空間呼叫 (React.useEffect(...)) 以及 useEffect 的包裝函式 (wrappers)。

  2. 將每個 effect 分類,並按以下偏好順序取代:

    1. 渲染時計算 (render-time calculation)
    2. 伺服器、載入器或框架資料 API (server, loader, or framework data API)
    3. 倉庫的伺服器狀態層(例如,一個帶有變數鍵和傳遞 signal 參數的 TanStack Query queryOptions 工廠)
    4. 事件處理器、動作或突變 (event handler, action, or mutation)
    5. 以 key 為基礎的元件邊界 (keyed component boundary)
    6. useSyncExternalStore
    7. 經過審查、用於真實外部系統的特定領域 hook

    如果上述選項均不適用,請在保留 effect 之前說明相關的外部同步契約。 取代方式 涵蓋了非顯而易見的情況。

  3. 保留倉庫的框架、資料層、lint 配置與相依性政策。僅在請求或倉庫政策授權時才添加函式庫;若沒有伺服器狀態層,則將剩餘的 effect 視為設計缺口。

  4. 執行 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。

相關技能