-
Notifications
You must be signed in to change notification settings - Fork 50.6k
Description
Do you want to request a feature or report a bug?
Report a bug.
What is the current behavior?
ReactDOM keeps references to previous states/props/children when component gets updated. All in all consuming three times as much memory as it really needed.
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:
Link to the example below (using production versions of react and react-dom):
https://codesandbox.io/s/epic-bartik-pvgqx.
Consider following example:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
let dataInstanceCount = 0;
class MyBigData {
constructor() {
const id = `my-big-data:${dataInstanceCount++}`;
this.getMyDataId = () => id;
this.data = new Array(100000).fill('');
}
}
let componentInstanceCount = 0;
class MyItem extends React.Component {
constructor(props) {
super(props);
this._myItemId = `my-item:${componentInstanceCount++}`;
this.state = {list: []};
}
render() {
return this.props.item.getMyDataId();
}
}
class MyApp extends React.Component {
constructor(props) {
super(props);
this.state = {list: []};
}
componentDidMount() {
this.updateList(() => {
this.updateList(() => {
this.updateList();
});
});
}
updateList(callback) {
this.setState({
list: [new MyBigData()]
}, callback);
}
render() {
return this.state.list.map((item) => (
<MyItem key={item.getMyDataId()} item={item} />
));
}
}
const rootElement = document.getElementById('root');
ReactDOM.render(
<MyApp />,
rootElement
);
setTimeout(() => {
console.log(
rootElement._reactRootContainer._internalRoot.current.alternate.firstEffect.memoizedProps.item.getMyDataId(),
rootElement._reactRootContainer._internalRoot.current.alternate.firstEffect.stateNode._myItemId
);
// > my-big-data:0, my-item:0
console.log(
rootElement._reactRootContainer._internalRoot.current.firstEffect.memoizedProps.item.getMyDataId(),
rootElement._reactRootContainer._internalRoot.current.firstEffect.stateNode._myItemId
);
// > my-big-data:1, my-item:1
console.log(
rootElement._reactRootContainer._internalRoot.current.lastEffect.memoizedProps.item.getMyDataId(),
rootElement._reactRootContainer._internalRoot.current.lastEffect.stateNode._myItemId
);
// > my-big-data:2, my-item:2
}, 1000);
I expect only one MyBigObject and one MyItem component to be in the memory. But instead I can see three of each in memory heap snapshot.
UPDATE
As shown in the updated example the references to these objects and components can be accessed in the sub-properties of the root DOM element.
What is the expected behavior?
There's no justifiable reason to keep in memory unmounted components and previous states/props of component after it was updated.
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
React 16.9.0, ReactDOM 16.9.0 (Production versions)
Mac/Win