admin管理员组

文章数量:1323723

EDIT It appears that this is due to an issue in React and is being fixed in a future release.


Given a table in react that displays data from an API which can be refreshed with pletely new information, I observed a detached DOM node leak (observe the green numbers):

Here is the code executed in the gif (code included below for posterity). To see the leak, go to the full page, open chrome dev tools, view the "Performance Monitor" tab and click the "Regen" button quickly as seen in the gif.

In this codesandbox, where the nodes are not generated in a loop, the leak does not occur.

The only difference is the {rows} array within the jsx. The confounding part is that {rows} is not a global variable, so I don't see how it would prevent the old nodes from being GC'd.

Why does using the local variable rows result in a detached DOM node leak?

Note: The DOM Nodes seem to settle at 21,000 but there shouldn't be that many nodes anyway, as you can see it starts at 7,000 after the first table generation. In my real-world app, these detached nodes persist even through navigation (with react router) which leads me to believe that it is an actual leak and not just nodes waiting to be GC'd.


Full code simulating the leak:

import React, { useState } from "react";

export default function App() {
  const [count, setCount] = useState(0);
  return (
    <div className="App">
      <button onClick={() => setCount(prev => prev + 1)}>Regen</button>
      <FTable count={count} />
    </div>
  );
}

function Cell() {
  const num = Math.floor(Math.random() * 100);
  return <td>{num}</td>;
}
function FTable(props) {
  const { count } = props;
  const rows = [];
  if (count > 0) {
    for (let i = 0; i < 1000; i++) {
      rows.push(
        // Use a different key for each time the
        // table is regenerated to simulate a new API
        // call bringing in new data
        <tr key={`${i} ${count}`}>
          <Cell row={i} />
          <Cell row={i} />
          <Cell row={i} />
        </tr>
      );
    }
  }
  return (
    <div>
      <table>
        <tbody>{rows}</tbody>
      </table>
    </div>
  );
}

EDIT It appears that this is due to an issue in React and is being fixed in a future release. https://github./facebook/react/issues/18066


Given a table in react that displays data from an API which can be refreshed with pletely new information, I observed a detached DOM node leak (observe the green numbers):

Here is the code executed in the gif (code included below for posterity). To see the leak, go to the full page, open chrome dev tools, view the "Performance Monitor" tab and click the "Regen" button quickly as seen in the gif.

In this codesandbox, where the nodes are not generated in a loop, the leak does not occur.

The only difference is the {rows} array within the jsx. The confounding part is that {rows} is not a global variable, so I don't see how it would prevent the old nodes from being GC'd.

Why does using the local variable rows result in a detached DOM node leak?

Note: The DOM Nodes seem to settle at 21,000 but there shouldn't be that many nodes anyway, as you can see it starts at 7,000 after the first table generation. In my real-world app, these detached nodes persist even through navigation (with react router) which leads me to believe that it is an actual leak and not just nodes waiting to be GC'd.


Full code simulating the leak:

import React, { useState } from "react";

export default function App() {
  const [count, setCount] = useState(0);
  return (
    <div className="App">
      <button onClick={() => setCount(prev => prev + 1)}>Regen</button>
      <FTable count={count} />
    </div>
  );
}

function Cell() {
  const num = Math.floor(Math.random() * 100);
  return <td>{num}</td>;
}
function FTable(props) {
  const { count } = props;
  const rows = [];
  if (count > 0) {
    for (let i = 0; i < 1000; i++) {
      rows.push(
        // Use a different key for each time the
        // table is regenerated to simulate a new API
        // call bringing in new data
        <tr key={`${i} ${count}`}>
          <Cell row={i} />
          <Cell row={i} />
          <Cell row={i} />
        </tr>
      );
    }
  }
  return (
    <div>
      <table>
        <tbody>{rows}</tbody>
      </table>
    </div>
  );
}

Share Improve this question edited Feb 20, 2020 at 19:10 Jordan asked Feb 12, 2020 at 21:37 JordanJordan 4,8235 gold badges28 silver badges37 bronze badges 2
  • 1 Have you tried this locally? I wonder if it could be caused by some sort of infinite loop safeguard on codesandbox? – JayB Commented Feb 12, 2020 at 21:56
  • @JayB yes the problem first occurred locally – Jordan Commented Feb 13, 2020 at 13:08
Add a ment  | 

1 Answer 1

Reset to default 7 +50

At first, I thought this is a bug with Hooks API. Because if you replace the <FTable count={count} /> with <FTable count={1} /> then bug will gone. But this is not a solution.

There is an issue about unexpected behavior with Hooks. But in this case, instead of DOM Nodes, the JS Heap size is growing.

Then I thought "okay, I'll try this case with class ponent" and I did this demo. The same problem is still here. Okay, what if this problem was introduced along with Hooks in version 16.3? But, no. The same issue exist in 16.0.

And then I realize. The key question is what is mon between all of these cases? The key!

Documentation says:

The best way to pick a key is to use a string that uniquely identifies a list item among its siblings.

Turned out, that React doesn't "Garbage Collect" old nodes if the key is unique on each render (well, in this case). That's why if you're using <tr key={i}> then everything is okay because React "rewrite" those nodes, and when you're using ${i * count} or ${i} ${count} or "whatever unique on every render", then nodes will be in memory. After some point, old nodes will be replaced with new ones, but I guess this is browser related behavior, not React. But I'm not a react expert and I don't know, where and how exactly this happens.

At this point, you can create an issue on GitHub and be aware of this problem.

本文标签: javascriptDetached DOM node memory leak in ReactStack Overflow