Communitygithub.com

jaimegago/agent-skills

在前端執行時期偵錯時使用,當 React UI 未反映已知正確的資料時——元件渲染空白或過時內容,即使 API 回傳正確回應、react-query / 資料擷取 hook 似乎卡住,或網路面板顯示成功的 200 但 UI 從未更新。用於診斷此類執行時期/時序行為,透過檢查即時狀態,而非建置或樣式 UI。不適用於建置/編譯錯誤、純樣式問題,或 API 確實回傳錯誤或遺失資料的情況。

agent-skills 是什麼?

這個技能專注於診斷前端執行時期錯誤,特別是當資料在 API 層級正確,但 UI 卻未正確渲染時。它協助開發者與 AI 代理(如 Claude Code、Cursor、Codex)在 React 應用程式中,逐步排除這類時序或執行時期錯誤。 其核心方法是打破「直覺性重新讀取原始碼」的習慣,改為優先檢查即時執行時期狀態。技能特別強調使用 React Query 時,網路面板顯示 200 OK 可能具有誤導性,因為資料可能未實際送達元件狀態。提供詳細決策樹,引導判斷問題是出在資料路徑中斷,還是查詢在中途被棄置(如因快取清除、重新掛載或無效化)。 技能支援 React 與 @tanstack/react-query v5.90,並提供基於 React Fiber 樹的 JavaScript 程式碼範例,用於從執行中的頁面讀取即時查詢觀察者狀態,並強制重新擷取以驗證查詢函式是否正常。這些程式碼範例為版本敏感且依賴非公開內部 API,故應視為起點進行調整。 此技能明確區分其適用範圍:不處理建置/編譯錯誤、純樣式問題,或 API 本身回傳錯誤資料的情況。其價值在於建立一套結構性流程,針對「API 正確但 UI 錯誤」這類難以追蹤的執行時期問題,進行有效定位與診斷。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/jaimegago/agent-skills/tree/HEAD/frontend-runtime-debugging

在你喜歡的 AI 中提問

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

說明文件

前端執行時期除錯:在重新讀取原始碼之前,先檢查即時狀態

資料在網路上是正確的,但 UI 錯了。這是一個執行時期/時序錯誤,而會浪費你時間的直覺是重新讀取原始碼檔案。請抵抗這個直覺。重新調整你的反應順序。

使用時機

在除錯過程中、症狀出現時才調用——而不是在任務開始時。當下列情況全部成立時使用:

  • React 元件渲染空白、過時或空的內容。
  • 所需的資料在來源端是已知正確的(API/後端回傳了正確的回應)。
  • 差距存在於「網路上的正確回應」與「螢幕上的正確渲染」之間。

典型描述:「API 回傳了正確資料,但元件是空白的」、「react-query 卡住了」、「請求是 200,但 UI 從未更新」、「重新整理後可以運作,但首次載入不行」。

這不適用於

  • 建置/編譯錯誤 — 那是工具鏈的錯誤,不是執行時期的錯誤。
  • 純樣式/版面問題 — 資料有渲染,只是看起來不對。
  • API 確實回傳錯誤或遺失資料 — 那是普通的後端/合約錯誤。請先確認網路回應是否正確;如果回應確實錯誤或為空,請停下來修正後端/合約。此技能僅適用於網路回應正確但 UI 不正確的情況。

重新排序(最重要的一課)

  1. 在重新讀取原始碼之前,先檢查即時執行時期狀態。 直覺反應是重新讀取元件、schema、佈局和 provider 的接線,以尋找錯誤。當資料在網路上正確但 UI 錯誤時,這個直覺反應是個陷阱:錯誤通常不在單一原始碼檔案中,而是在多個各自正確的片段之間的執行時期/時序互動中。原始碼審查在結構上無法看到這類問題。請先查看即時狀態。

  2. 網路面板中的 200 可能具有誤導性。 顯示 200 的請求表示回應已送達。這並不表示資料已到達元件或已提交至快取。回應可能送達後被丟棄、移除或從未提交。不要從 200 就斷定「擷取正常運作」——請確認資料確實已到達消費元件的狀態。

  3. 對於 react-query,請檢查受影響元件的即時查詢觀察者 (query observer),而不僅僅是網路。 讀取觀察者的 status、fetchStatus、dataUpdateCount 和 error。

    • 查詢卡在 status: "pending" / fetchStatus: "fetching" 且 dataUpdateCount: 0 表示 queryFn promise 的結果從未提交給觀察者。這指向查詢在中途被移除或無效化——並非擷取失敗。
    • 透過強制觀察者重新擷取 (refetch) 來確認 queryFn 本身是否健康。如果手動重新擷取能立即且正確地解決問題,那麼擷取/客戶端/schema 都沒有問題,真正的錯誤是初始查詢被棄置了(因快取清除、effect 無效化或重新掛載而移除)。這個單一觀察就能區分「資料路徑中斷」與「資料路徑正常但查詢被終止」——這是完全不同的錯誤。
  4. 一旦定位到「查詢在中途被棄置」,請向上游查看生命週期/快取管理。 原因是在查詢啟動的同一時刻執行的某些動作:removeQueries / invalidateQueries / 快取重置,或掛載/卸載/重新掛載的 race condition。請查看驗證/ session 轉換的 effect、主體/租戶切換的清除動作,以及 provider 的重新掛載——不要查看擷取程式碼。

決策路徑

UI 錯誤,但資料來源端正確?
  → 確認網路回應確實正確(如果不是,這不是本技能的職責)
  → 讀取受影響元件的即時查詢觀察者
       狀態為 pending + fetchStatus 為 fetching + dataUpdateCount 為 0?
          → 對該觀察者強制重新擷取
               重新擷取能立即且正確地解決?
                  → queryFn 沒問題。錯誤 = 初始查詢被棄置。
                    向上游查找:removeQueries/invalidateQueries/reset、
                    重新掛載/卸載 race condition、驗證/session/租戶清除 effect。
               重新擷取也失敗?
                  → 資料路徑確實中斷。檢查 queryFn/客戶端/schema。
       有資料但 UI 仍然空白?
          → 元件中的渲染/選擇器/條件錯誤,現在可以重新讀取原始碼。

即時檢查工具(指引,非穩定 API)

以下程式碼片段可從執行中的頁面,透過遍歷 React Fiber 樹從渲染的 DOM 元素開始,讀取元件的 react-query 觀察者,取得其當前的查詢狀態,並強制重新擷取以證明 queryFn 正常運作。

這些片段對版本敏感。 它們是根據 @tanstack/react-query v5.90 和 React 18 的 fiber 內部實作推導出來的,依賴於未公開的內部 API,並且會隨著版本偏移而失效。請將它們視為需要調整的起點——在使用前,請根據專案安裝的版本,重新驗證欄位名稱和 fiber 的結構。它們是指引,而非保證穩定的 API。

從 DOM 節點遍歷 Fiber 樹以尋找 hooks/觀察者:

// 選取受影響元件渲染的一個 DOM 元素(例如,透過開發者工具中的 $0)。
const el = $0; // 或 document.querySelector('[data-testid="..."]')
const key = Object.keys(el).find(k => k.startsWith('__reactFiber$'));
let fiber = el[key];

// 向上/向下遍歷 fiber 樹;memoizedState 是一個 linked list 的 hooks。
// react-query 將其觀察者儲存在某個 hook 的 memoizedState 上。
function* fibers(root) {
  const stack = [root];
  while (stack.length) {
    const f = stack.pop();
    if (!f) continue;
    yield f;
    if (f.child) stack.push(f.child);
    if (f.sibling) stack.push(f.sibling);
  }
}

// 掃描 hook 狀態,尋找看起來像 QueryObserver 的東西。
function findObservers(fiber) {
  const found = [];
  for (const f of fibers(fiber)) {
    let hook = f.memoizedState;
    while (hook) {
      const s = hook.memoizedState;
      if (s && typeof s === 'object') {
        // QueryObserver 實例會暴露 getCurrentResult()/getCurrentQuery()。
        if (typeof s.getCurrentResult === 'function') found.push(s);
        // 某些版本會巢狀包裝;也要檢查常見的包裝器。
        if (s.observer && typeof s.observer.getCurrentResult === 'function') found.push(s.observer);
      }
      hook = hook.next;
    }
  }
  return found;
}

const observers = findObservers(fiber);

讀取觀察者的即時查詢狀態:

const obs = observers[0];
const r = obs.getCurrentResult();
console.log({
  status: r.status,            // 'pending' | 'error' | 'success'
  fetchStatus: r.fetchStatus,  // 'fetching' | 'paused' | 'idle'
  dataUpdateCount: obs.getCurrentQuery().state.dataUpdateCount,
  error: r.error,
});
// pending + fetching + dataUpdateCount 0  ==> 查詢在中途被棄置,而非擷取失敗。

強制重新擷取以證明 queryFn 健康:

obs.refetch().then(r => console.log('refetch:', r.status, r.data)).catch(e => console.log('refetch failed:', e));
// 立即且正確地解決  ==>  queryFn/客戶端/schema 沒問題;
//                       原始查詢是在上游被終止的。

如果專案暴露了 QueryClient(例如,為了除錯而放在 window 上,或可從 provider 的 fiber 取得),則優先使用 queryClient.getQueryCache().findAll() 並直接檢查每個查詢的 state 和 observers——這比遍歷 fiber 更穩健。

完成

當你定位到錯誤時:

  1. 說明它落在決策分支的哪一側:「資料路徑中斷」還是「查詢被棄置」。
  2. 如果是被棄置,指出是哪個上游生命週期/快取動作導致。
  3. 只有到這時,才重新讀取負責的特定原始碼檔案——現在你知道是哪一個了。

Individual skills in this repo

This repo contains 1 individual skill — each has its own dedicated page.

相關技能