在写 react 代码的时候经常遇到如下的报错
Can't perform a React state update on an unmounted component. This is a no-op......
本篇文章首先回顾一下什么是内存泄露,然后看两个 demo 观察 react 出现内存泄露的具体情况。
什么是内存泄露
程序的运行需要内存。只要程序提出要求,操作系统或者运行时(runtime)就必须供给内存。
对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。
不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)。
JavaScript 中常见的几种内存泄露
全局变量引起的内存泄漏
function leaks(){
leak = '***';
闭包引起的内存泄漏
var leaks = (function(){
var leak = '***';
return function(){
console.log(leak);
dom清空或删除时,事件未清除导致的内存泄漏
document.querySelector("#demo").addEventListener('click', myFunction);
var para1=document.querySelector("#demo");
para1.parentNode.removeChild(para1);
如果我们在没有取消 click 方法前去销毁了 para1 节点,就会造成内存泄露。
正确的做法:
document.querySelector("#demo").addEventListener('click', myFunction);
document.querySelector("#demo").removeEventListener("click", myFunction);
var para1=document.querySelector("p1");
para1.parentNode.removeChild(para1);
延展阅读,大家有空可以仔细的看看阮一峰老师的相关文章
www.ruanyifeng.com/blog/2017/0… (阮一峰)
为了更加了解 react 的内存泄露问题看看下面几个 demo
Demo 1:
componentWillMount: function () {
var onLogin = this.props.onLogin || function () {},
onLogout = this.props.onLogout || function () {};
this.on('authChange', function () {
console.log('user authenticated:', this.state.isAuthenticated);
return this.state.isAuthenticated
? onLogin(this.state)
: onLogout(this.state);
}.bind(this));
上面的例子是在 Stack Overflow 上看到的,楼主在componentWillMount
的时候挂载了authChange
事件,然后 react 出现了如下的报错:
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 the componentWillUnmount method”
意思为:我们不能在组件销毁后设置state,防止出现内存泄漏的情况
需要怎么解决啦?
添加如下代码即可
componentWillUnmount: function () {
this.off('authChange', this.authChange);
this.authChange = null;
很明显这种情况就是在 dom 结构销毁的时候,事件却没有清除导致的内存泄漏,所以我们需要在componentWillUnmount
的时候去清除挂载的方法
react 内存泄露相关解释和解决方法
这里就提到了内存泄露,当我们在使用事件绑定,setInterval,setTimeOut 或一些函数的时候,但是却没有在组件销毁前清除的时候会造成内存泄露。这里我们手动的再componentWillUnmount
去清除相关的方法即可。
I would move your function into componentDidMount and add cleanup on componentWillUnmount
Important: componentWillMount is called on the server and client, but componentDidMount is only called on the client.
If you’re using eventListeners, setInterval or other functions that needs to be cleaned, put them in componentDidMount. The server will not call componentWillUnmount and is usually the cause of memory leaks.
stackoverflow.com/questions/4…
Demo 2:
下面这种就是常见的情况:
this.pwdErrorTimer = setTimeout(() => {
this.setState({
showPwdError:false
}, 1000);
设置了一个timer延迟设置state,然而在延迟的这段时间,组件已经销毁,则造成此类问题
解决方法:
利用生命周期钩子函数:componentWillUnmount
componentWillUnmount(){
clearTimeout(this.pwdErrorTimer);
clearTimeout(this.userNameErrorTimer);
如果引入了 react16.8+
我们完全可以使用 useEffect() 函数解决大部分内存泄露的问题(官网-useEffect-文档)
文档中提到了两个重要的概念
为什么要在 effect 中返回一个函数?
这是 effect 可选的清除机制。每个 effect 都可以返回一个清除函数。如此可以将添加和移除订阅的逻辑放在一起。它们都属于 effect 的一部分。
React 何时清除 effect?
React 会在组件卸载的时候执行清除操作。正如之前学到的,effect 在每次渲染的时候都会执行。这就是为什么 React 会在执行当前 effect 之前对上一个 effect 进行清除。我们稍后将讨论为什么这将助于避免 bug以及如何在遇到性能问题时跳过此行为。
如果你熟悉 React class 的生命周期函数,你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。
关于 promise 请求是否会造成内存泄露的问题
1、Does never resolved promise cause memory leak?
Does never resolved promise cause memory leak?
原问题在上面,可以看看
2、Memory leaks in loops with Promise ?
循环一个 promise 造成内存泄露?