React-防止內存泄漏處理

問題描述

原博客 http://www.czhuangjia.top/blog
在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
相關文章
相關標籤/搜索