React state update memory leak memory leak problems and solutions

Background problem

React in the test page, previously found in a page of data has been loaded, immediately switch to another page, React gives the following warning in the console:

Warning: Can’t perform a React state update on an unmounted component.
This is a no-op, but it indicates a memory leak in your application.
To fix, cancel all subscriptions and asynchronous tasks in a useEffect
cleanup function.

problem causes

This situation caused a lot of problems, but ultimately it is a reason:

React perform the setState () operation on the assembly has been unloaded (unmounted) a.

By the use of Chrome's Developer Tool observation of memory found before the data is loaded to switch pages, after the memory usage is going up would not come down, it indicates that the phenomenon of memory leaks.

JS memory usage

Two kinds of common situations

There are two common usage is very easy to cause the problem.

  1. Use the setTimeout (), and execute setState in the callback function (). If you leave the page before the callback function is executed causes the target component to be uninstalled, then the callback function is executed when the memory overflow error occurs.
setTimeout(function () {
	this.setState(data);
}, 1000);
  1. SetState callback performed in an asynchronous request (), before the callback is executed to complete the assembly of the target was unloaded. Here we are with asynchronous requests axios example:
axios.get(url)
.then(response => {
	this.setState(response.data);
});

Solution

The solution is to ensure that when the components are unloaded, with the component relating to setTimeout () and asynchronous request must be cleared together.

React We all know that there are two types of components:

  • Class Component
  • Functional Component

The following will demonstrate solutions for both types of components, respectively.

Solve setTimeout () memory leak problem

Here we can use clearTimeout () function to clear the setTimeout before we execute (). Since the setTimeout () returns a timeout id, we only need to pass this timeout id clearTimeout () in on it.

// Class Component
componentDidMount() {
	this.timeout = setTimeout(function () {
		setState(data);
	}, 1000);
}

componentWillUnmount() {
	// 组件即将卸载时先清除 setTimeout()
	clearTimeout(this.timeout);
}
// Functional Component
useEffect(() => {
	const timeout = setTimeout(function () {
		setState(data);
	}, 1000);
	
	// 返回 clean up 函数。在组件即将被卸载时,React 会执行该函数
	return () => {
		clearTimeout(timeout);
	};
}, []);
Solving Memory overflow caused by an asynchronous request

Here we have two solutions for the Class Component, we can set a value to determine whether the component is loaded. If the value is false, then the asynchronous request our callback function returns immediately without doing anything.

// Class Component
constructor(props) {
	super(props);
	// 该值表示组件是否已加载
	this._isMounted = false;
}

componentDidMount() {
	// 组件已加载,设为 true
	this._isMounted = true;

	axios.get(url)
	.then(data => {
		// 如果组件还没加载,或已经卸载,则直接返回
		if (!this._isMounted) {
			return;
		}
		// ... rest of codes
	});
}

componentWillUnmount() {
	// 组件即将卸载,将值设为 false
	this._isMounted = false;
}

Of the Functional Component, we can not use the above method. Because we do not have a target value this._isMounted can help us save, every component re-rendering time, Functional Component code will be re-run again, lead us to set isMounted value is refreshed.

If our request is to use asynchronous axios, we can use the cancel token axios offered to cancel the asynchronous request.

// Functional Component
const source = axios.CancelToken.source();
const cancelToken = .source.token;

useEffect(() => {
	axios.get(url, {
		cancelToken: cancelToken
	})
	.then(data => {
		// ... working with states
	})
	.catch(error => {
		// 如果取消异步请求的话,一定要写出 catch 块,才不会出现 uncaught exception 的错误
	});
	
	return () => {
		// 组件即将卸载时,取消异步请求
		source.cancel();
	};
}, []);
Published 14 original articles · won praise 8 · views 2203

Guess you like

Origin blog.csdn.net/vandavidchou/article/details/103382877