@samitouri / QOS-React / commits / f23aa1d9f5

[DevTools] Fix memory leak when unmounting hoistables (#35741)

Sebastian "Sebbie" Silbermann committed Feb 10, 2026 at 13:09 UTC f23aa1d9f5034e7cfbd3e6015ce102bb72a005a0
2 files changed +56 -1
packages/react-devtools-shared/src/__tests__/store-test.js
+55
@@ -3616,4 +3616,59 @@ describe('Store', () => {
3616 <div>
3617 `);
3618 });
3619 +
3620 + // @reactVersion >= 19
3621 + it('cleans up host hoistables', async () => {
3622 + function Left() {
3623 + return (
3624 + <style href="test.css" precedence="medium">
3625 + {'* {color:black}'}
3626 + </style>
3627 + );
3628 + }
3629 +
3630 + function Right() {
3631 + return (
3632 + <style href="test.css" precedence="medium">
3633 + {'* {color:black}'}
3634 + </style>
3635 + );
3636 + }
3637 +
3638 + await actAsync(() => {
3639 + render(
3640 + <>
3641 + <Left />
3642 + <Right />
3643 + </>,
3644 + );
3645 + });
3646 +
3647 + // Ensure we're still testing deduplicated hoistables.
3648 + expect(document.head.querySelectorAll('style')).toHaveLength(1);
3649 + expect(store).toMatchInlineSnapshot(`
3650 + [root]
3651 + <Left>
3652 + <Right>
3653 + `);
3654 + let style = document.head.querySelector('style');
3655 + let styleID = agent.getIDForHostInstance(style).id;
3656 + expect(store.containsElement(styleID)).toBe(true);
3657 +
3658 + await actAsync(() => {
3659 + render(
3660 + <>
3661 + <Right />
3662 + </>,
3663 + );
3664 + });
3665 +
3666 + expect(store).toMatchInlineSnapshot(`
3667 + [root]
3668 + <Right>
3669 + `);
3670 + style = document.head.querySelector('style');
3671 + styleID = agent.getIDForHostInstance(style).id;
3672 + expect(store.containsElement(styleID)).toBe(true);
3673 + });
3674 });
packages/react-devtools-shared/src/backend/fiber/renderer.js
+1 -1
@@ -991,8 +991,8 @@ function releaseHostResource(
991 // eslint-disable-next-line no-for-of-loops/no-for-of-loops
992 for (const firstInstance of resourceInstances) {
993 publicInstanceToDevToolsInstanceMap.set(
994 + publicInstance,
995 firstInstance,
995 - nearestInstance,
996 );
997 break;
998 }