Hacktoberfest 2026:維護者為十月標記出來的 issue,仍然開放、適合新手。 瀏覽 Hacktoberfest issue

Fast Refresh doesn't reset the React Compiler memo cache from c()

已關閉 適合新手
#69 0 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視

維護者通常 1 天內回覆

還沒有人認領這個 Issue。

評估

難度
2/5
預估耗時
1-3 小時
新手友好度
82/100
Issue 類型
缺陷
描述清晰度
描述清楚
活躍度
活躍
技術堆疊
react, typescript
領域
frontend

研究方向

從 src/react/compiler-runtime.ts 開始,檢查 c() 如何儲存其 memo 快取;將它與 src/dom/dispatcher.ts 中的 refresh 處理進行比較。將重現案例新增至 tests/fast-refresh.test.tsx,並確認更新後的元件在保留 state 的同時先顯示 after:1,再顯示 after:2。Issue 回報指出,採用建議的變更後,此測試和完整測試套件都能通過。

由索引模型根據 Issue 內容生成。

描述

Summary

After a Fast Refresh update, components compiled by React Compiler keep rendering their old output. The compiler runtime's c() keeps its memo cache across the refresh, so cached JSX from the previous version of the component is returned until a full reload.

React resets this cache on refresh. In development, renderWithHooks sets ignorePreviousDependencies when the fiber's type changed, and useMemoCache then refills the cache with sentinels (ReactFiberHooks.js).

Reproduction

Added to tests/fast-refresh.test.tsx. The component is written the way the compiler emits it:

import { c } from 'react/compiler-runtime'

it('resets the React Compiler memo cache but preserves state', () => {
  function version(label: string) {
    return function Example() {
      const $ = c(3)
      const [count, setCount] = useState(0)
      let t0
      if ($[0] === Symbol.for('react.memo_cache_sentinel')) {
        t0 = () => setCount(n => n + 1)
        $[0] = t0
      } else t0 = $[0] as () => void
      let t1
      if ($[1] !== count) {
        t1 = <button onClick={t0}>{label}:{count}</button>
        $[1] = count
        $[2] = t1
      } else t1 = $[2]
      return t1
    }
  }
  const Before = version('before'), update = family(Before, 'stable')
  const { container } = setup(<Before />)
  click(container)
  update(version('after'))
  Refresh.performReactRefresh()
  expect(container.textContent).toBe('after:1')
  click(container)
  expect(container.textContent).toBe('after:2')
})

On main it fails with expected 'before:1' to be 'after:1'.

Cause

c() in src/react/compiler-runtime.ts stores the cache in a useRef, which survives the refresh. useMemo and useCallback already handle this case: depsEqual returns false while the fiber is refreshing.

Babel's compiler hides the bug in development because it also emits a source-hash check that clears the cache whenever the file changes. The Rust port in oxc-transform-react, which @vitejs/plugin-react uses for react({ compiler: true }), doesn't emit that check, so with it every hot update of a compiled component is stale.

Suggested fix

Back c() with useMemo, so it picks up the existing refresh handling in depsEqual:

export function c(size: number): Array<unknown> {
  return useMemo(() => new Array<unknown>(size).fill(MEMO_CACHE_SENTINEL), [size])
}

With this change the test above passes and the rest of the suite still passes (1860 passed, 91 skipped).

Versions
  • @tanstack/redact 0.2.0, and main at d56d87cb
  • react-refresh 0.19.0 (from the test suite)
  • oxc-transform-react 0.155.0 with @vitejs/plugin-react 6.1.2, where we first hit it

Disclaimer: this issue, including the reproduction test and the suggested fix, was researched and written by Claude, an AI assistant from Anthropic.

主要語言
TypeScript
星號
408
分支
13
平均合併
1 小時 44 分鐘
30 天內合併 PR
21

環境準備

這個專案沒有提供開發容器、Dockerfile 或貢獻指南,環境需要你自己搭建:先看它的 README,通用步驟見我們的新手貢獻指南。

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

TanStack/redact 的其他 Issue

查看 TanStack/redact 的全部 Issue

相似的 Issue

更多 TypeScript Issue

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。