facebook/react

Bug: initial props will leak if a component renders nested elements, when an outer element never rerenders.

onnlucky opened this issue · 1 comments

JSX code like this will leak that something value:

<div>
  <SubComponent foo={something} />
</div>

As the component and the sub component re-render. That div will never change, and will hold on to that initial prop via div.__reactProps$[SECRET].children[0].props.foo.

This is not only unexpected. In cases it can be very difficult to work around.

Here at Framer (http://framer.com) we use a tree like structure that represents the user document. Which potentially can be huge, like 1G or more. Sometimes this has to be reloaded from the server. At that point we cannot afford having the old document lingering around. But it is react that will continue to retain it due to this issue.

React version: 18.2.38 (But has been in react since at least 17.)

See also: #25658 (now closed as stale) and other reports filed in the past. I hope that by filing this one, with such a small reproduction, helps someone to dig in and hopefully fix it? 🙏

Steps To Reproduce

Click the "next" button in the code example. That switches from a huge array to a large one. But the memory use won't drop.

Link to code example: https://codesandbox.io/p/sandbox/leak-static-element-children-props-forked-zdm83w

Or the simplified version:

import React from "react";

function SubComponent({ prop }: { prop: number[] }) {
  return <p>array length: {prop.length}</p>;
}

function Test({ prop }: { prop: number[] }) {
  return (
    <div className="holder">
      <SubComponent prop={prop} />
    </div>
  );
}

export default function App() {
  // Initially, a very large state.
  const [state, setState] = React.useState(
    Array.from({ length: 20_000_000 }, () => Math.random())
  );

  return (
    <>
      <Test prop={state} />
      <button
        onClick={() => {
          // Any next state is a lot smaller.
          setState(Array.from({ length: 1_000_000 }, () => Math.random()));
        }}
      >
        Next
      </button>
    </>
  );
}

The current behavior

Leaks memory:
memory-leak

The expected behavior

React internals should not be retaining properties that the code using the library has moved away from.

@tomocchino any idea who I can bug about this one? We're happy to help.