React防止內存泄漏

在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,防止出現內存泄漏的情況。

//組件B
    class TestContainer extends Component{
        constructor(){
            super()
            this.state = {
                isShow:true
            }
        }
        render(){
            return (
                <div>
                    <button onClick={()=>this.setState({isShow:!this.state.isShow})}>toggle</button>
                    {!!this.state.isShow&&<Test />}
                </div>
            )
        }
    }
    //組件A
    class Test extends Component{
        constructor(){
            super()
            this.state = {
                num:0
            }
        }
        getNum=()=>{
            //模擬異步請求
            this.timer = setTimeout(()=>{
                this.setState({ num: Math.random() })
            },3000)
        }
        render(){
            return (
                <div onClick={this.getNum} style = {{ width: 100, height: 100, background: 'red' }}>
                    {this.state.num}
                </div>
            )
        }
    }
  // 在本例子中:
        // 當我們點擊組件A時,會發送一個異步請求,請求成功後會更新num的值。
        // 當我們點擊組件B時,會控制組件的A的卸載與裝載

當我們點擊組件A後,組件A需要3秒的時間才能獲取到數據並重新更新num的值,假如我們在這3秒內點擊一次組件B,
表示卸載組件A,但是組件A的數據請求依然還在,當請求成功後,組件A已經不存在,此時就會報這個警告(大概意思就是:你組件都沒了,你還設置個啥) 

解決辦法:

本問題出現的原因就是:我們應該在組件銷燬的時候將異步請求撤銷

  • 在componentWillUnmount中撤銷異步請求
  1. axios上有撤銷異步請求的方法,但是我們有這麼多組件,每次都要撤銷豈不是太麻煩了
  2. 我們可以利用一個‘開關的思想’,在組件銷燬的時候給this上掛載一個屬性,每次發送請求的時候,我們判斷一下這個屬性是否存在(還是麻煩,每次都要判斷)
  3. 基於思路2,我們不想每次判斷,因此是不是應該將其封裝,利用修飾器對componentWillUnmount和setState進行改裝
 function inject_unount (target){
        // 改裝componentWillUnmount,銷燬的時候記錄一下
        let next = target.prototype.componentWillUnmount
        target.prototype.componentWillUnmount = function () {
            if (next) next.call(this, ...arguments);
            this.unmount = true
         }
         // 對setState的改裝,setState查看目前是否已經銷燬
        let setState = target.prototype.setState
        target.prototype.setState = function () {
            if ( this.unmount ) return ;
            setState.call(this, ...arguments)
        }
    }
    @inject_unount
    class BaseComponent extends Component {
    
    }
    //以後我們寫組件時直接繼承BaseComponent

 轉載於:https://segmentfault.com/a/1190000017186299

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章