react: Memory leak - React DOM keeps references to previous states/props/children of component
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
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 16
- Comments: 16
Memory leak occurs when, you change the state of an unmounted component, so to remove this kind of warning, you better check is your component is mounted or not before updating its state. To do this =>
Create a property _isMounted and set it to false inside your components (that have states/Smart Component) : _isMounted = false;
Now set it to true, when your component gets mounted & set it to false when it gets unmounted:
componentWillMount() {
this._isMounted = true; }
componentWillUnmount() { this._isMounted = false; }
Now every time before updating the state, check whether component is mounted or not, for your case:
setInterval(() => { if(this._isMounted) { this.setState({ list: [new MyBigObject()] });
} }, 500);
Great that stalebot can close this if the developers simply ignore it long enough. Kudos, React Team!
Is there any update on this issue? We are seeing this as a significant issue when using Redux and container components. When our container componet(that is connected to redux store) passes props to the child components, and then the redux store updates. The child component props are being stranded in the dom with old reference(seen in the heap after forcing a garbage collection cycle). This is causing huge amounts of memory bloat when on a page that is connected to signalR for real time collaboration between users(as each redux update creates hundreds of stranded object references in the child components).
I have verified that this is the cause by instead having all of the previously “dumb” pure child components be converted to Connected components and pull their props from redux instead of having the container component pattern control all of the store connections. this then correctly all references the single redux store object and garbage collection works as expected without stranded references.
New issue opened here: https://github.com/facebook/react/issues/18790
Which has the main content of the original post, plus the description I added in the comments of this issue
I have an app with lots of components that get unmounted all the time and this is a big issue. I tried to reproduce this problem in a very simple example (https://jsfiddle.net/vne79fzs/), and I’m concerned how easy is to leak memory without developer being aware. As far as I know the problem occurs at least since 16.0 and is still there (16.12).
Is there any viable workaround? I tried to cleanup the state field, but the memory is hold by other fields.
Closing this issue after a prolonged period of inactivity. If this issue is still present in the latest release, please create a new issue with up-to-date information. Thank you!
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contribution.
I updated the example link. The external resources now point to react/react-dom 16.9.0 versions. The issue is still present.