react 記憶體洩露常見問題解決方案

Pandaaa發表於2019-04-03

寫在前面

  • 在寫 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 = '***'; //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);

// 我們需要在刪除節點前清除掛載的 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的時候去清除掛載的方法

react 記憶體洩露相關解釋和解決方法

  • 這裡就提到了記憶體洩露,當我們在使用事件繫結,setInterval,setTimeOut 或一些函式的時候,但是卻沒有在元件銷燬前清除的時候會造成記憶體洩露。這裡我們手動的再componentWillUnmount去清除相關的方法即可。

  • why:

    • 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);
}
複製程式碼

衍生閱讀

關於 promise 請求是否會造成記憶體洩露的問題

1、Does never resolved promise cause memory leak?

2、Memory leaks in loops with Promise ?

相關文章