Fast Refresh doesn't reset the React Compiler memo cache from c()
Les mainteneurs répondent en général sous 1 jour
Personne n'a encore pris cette issue.
Évaluation
- Difficulté
- 2/5
- Temps estimé
- 1-3 heures
- Accessibilité débutants
- 82/100
- Type d'issue
- Bug
- Clarté
- Clairement spécifiée
- Activité
- Active
- Stack technique
- react, typescript
- Domaine
- frontend
Piste de recherche
Commencez par src/react/compiler-runtime.ts et examinez comment c() stocke son cache de mémoïsation ; comparez cela au traitement du refresh dans src/dom/dispatcher.ts. Ajoutez la reproduction à tests/fast-refresh.test.tsx et vérifiez que le composant mis à jour affiche after:1, puis after:2, tout en préservant l’état. L’issue indique que ce test et l’ensemble de la suite passent avec la modification suggérée.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
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/redact0.2.0, andmainat d56d87cbreact-refresh0.19.0 (from the test suite)oxc-transform-react0.155.0 with@vitejs/plugin-react6.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.
- Langage dominant
- TypeScript
- Étoiles
- 408
- Forks
- 13
- Merge moyen
- 1 h 44 min
- PR mergées (30 j)
- 21
Préparer son environnement
Ce projet ne fournit ni conteneur de développement, ni Dockerfile, ni guide de contribution : l'installation est à votre charge. Commencez par son README, et consultez notre guide de la première contribution pour les étapes générales.
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de TanStack/redact
-
Difficulté 2/5 1-3 heures Accessibilité débutants 85/100
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 88/100
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 90/100
TanStack/redact#67 · 1 réaction ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 1/5 Moins d'une heure Accessibilité débutants 82/100
Les mainteneurs répondent en général sous 1 jour
Toutes les issues de TanStack/redact
Issues similaires
-
Difficulté 2/5 1-3 heures Accessibilité débutants 82/100
better-auth/better-auth#12132 · 1 réaction ·
Les mainteneurs répondent en général sous 1 jour
-
o8 next debug build aborts on Glass to Dark appearance switch (nil identifier messaged before check)Ouvertebug
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
hurttlocker/o8#5112 ·
-
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
webrecorder/wabac.js#386 ·
-
needs-triage
Difficulté 1/5 1-3 heures Accessibilité débutants 92/100
agent-of-empires/agent-of-empires#5098 · 1 commentaire · 1 réaction ·
Les mainteneurs répondent en général sous 1 jour