diff --git a/packages/react-reconciler/src/ReactFiberHotReloading.js b/packages/react-reconciler/src/ReactFiberHotReloading.js index 16d78a667709..6392440d05d3 100644 --- a/packages/react-reconciler/src/ReactFiberHotReloading.js +++ b/packages/react-reconciler/src/ReactFiberHotReloading.js @@ -296,7 +296,11 @@ function scheduleFibersWithFamiliesRecursively( if (staleFamilies.has(family)) { needsRemount = true; } else if (updatedFamilies.has(family)) { - needsRender = true; + if (tag === ClassComponent) { + needsRemount = true; + } else { + needsRender = true; + } } } } diff --git a/packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js b/packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js index 4d1e5bf85c9b..b9110d8cc79a 100644 --- a/packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js +++ b/packages/react-refresh/src/__tests__/ReactFreshIntegration-test.js @@ -1299,6 +1299,53 @@ describe('ReactFreshIntegration', () => { } }); + it('remounts deprecated factory components', () => { + if (__DEV__) { + expect(() => { + render(` + function Parent() { + return { + render() { + return ; + } + }; + }; + + function Child({prop}) { + return

{prop}1

; + }; + + export default Parent; + `); + }).toWarnDev( + 'The component appears to be a function component ' + + 'that returns a class instance.', + {withoutStack: true}, + ); + const el = container.firstChild; + expect(el.textContent).toBe('A1'); + patch(` + function Parent() { + return { + render() { + return ; + } + }; + }; + + function Child({prop}) { + return

{prop}2

; + }; + + export default Parent; + `); + // Like classes, factory components always remount. + expect(container.firstChild).not.toBe(el); + const newEl = container.firstChild; + expect(newEl.textContent).toBe('B2'); + } + }); + describe('with inline requires', () => { beforeEach(() => { global.FakeModuleSystem = {};