react 內存泄露常見問題解決方案

寫在前面

  • 在寫 react 代碼的時候常常遇到以下的報錯
Can't perform a React state update on an unmounted component. This is a no-op...... 複製代碼
  • 本篇文章首先回顧一下什麼是內存泄露,而後看兩個 demo 觀察 react 出現內存泄露的具體狀況。

什麼是內存泄露

  • 程序的運行須要內存。只要程序提出要求,操做系統或者運行時(runtime)就必須供給內存。javascript

  • 對於持續運行的服務進程(daemon),必須及時釋放再也不用到的內存。不然,內存佔用愈來愈高,輕則影響系統性能,重則致使進程崩潰。html

  • 再也不用到的內存,沒有及時釋放,就叫作內存泄漏(memory leak)。java

JavaScript 中常見的幾種內存泄露

  • 全局變量引發的內存泄漏
function leaks(){  
    leak = '***'; //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 節點,就會形成內存泄露。react

正確的作法:promise

document.querySelector("#demo").addEventListener('click', myFunction);

// 咱們須要在刪除節點前清除掛載的 click 方法
document.querySelector("#demo").removeEventListener("click", myFunction);

var para1=document.querySelector("p1");

para1.parentNode.removeChild(para1);
複製代碼

爲了更加了解 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的時候去清除掛載的方法bash

react 內存泄露相關解釋和解決方法

  • 這裏就提到了內存泄露,當咱們在使用事件綁定,setInterval,setTimeOut 或一些函數的時候,可是卻沒有在組件銷燬前清除的時候會形成內存泄露。這裏咱們手動的再componentWillUnmount去清除相關的方法便可。閉包

  • why:app

    • 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…dom

Demo 2:

  • 下面這種就是常見的狀況:
this.pwdErrorTimer = setTimeout(() => {
    this.setState({
        showPwdError:false
    })
}, 1000);
複製代碼

設置了一個timer延遲設置state,然而在延遲的這段時間,組件已經銷燬,則形成此類問題async

  • 解決方法:
  • 利用生命週期鉤子函數:componentWillUnmount
componentWillUnmount(){
    clearTimeout(this.pwdErrorTimer);
    clearTimeout(this.userNameErrorTimer);
}
複製代碼

若是引入了 react16.8+

文檔中提到了兩個重要的概念

爲何要在 effect 中返回一個函數?

  • 這是 effect 可選的清除機制。每一個 effect 均可以返回一個清除函數。如此能夠將添加和移除訂閱的邏輯放在一塊兒。它們都屬於 effect 的一部分。

React 什麼時候清除 effect?

  • React 會在組件卸載的時候執行清除操做。正如以前學到的,effect 在每次渲染的時候都會執行。這就是爲何 React 會在執行當前 effect 以前對上一個 effect 進行清除。咱們稍後將討論爲何這將助於避免 bug以及如何在遇到性能問題時跳過此行爲。

提示

若是你熟悉 React class 的生命週期函數,你能夠把 useEffect Hook 看作 componentDidMount,componentDidUpdate 和 componentWillUnmount 這三個函數的組合。

衍生閱讀

關於 promise 請求是否會形成內存泄露的問題

一、Does never resolved promise cause memory leak?

二、Memory leaks in loops with Promise ?

相關文章
相關標籤/搜索