[技術博客]React-Native中的組件加載、卸載與setState問題

React-Native中的組件加載、卸載與setState問題。html

Warning: Can only update a mounted or mounting component. This usually means you called setState, replaceState, or forceUpdate on an unmounted component. This is a no-op.react

一般咱們會在componentWillMount方法中執行異步數據請求,而後調用setState方法處理獲得的數據來更新界面。有時會遇到這個警告,以下圖:react-native

警告提示咱們可能在被卸載的組件上調用了setState()方法。通常狀況下是在某個異步請求還未結束時組件被卸載了,但請求完畢後按照仍會按照正常流程調用setState()方法,這時就出現了上面的警告。網絡

下面用一段代碼來複現這種狀況。爲了方便,不使用真正的網絡請求,而是使用下面的count()方法。count()方法每隔1秒調用一次setState方法將this.state.count加1,總共調用10次setState()方法。這樣就有充足的時間來觀察和操做。異步

在render()方法中,將this.state.count這個數字顯示在屏幕中央。componentWillMount方法中調用count方法,組件準備加載時就開始計數,至關於進行異步的網絡請求。在跳轉到這個頁面後,屏幕中央的數字會從0一直加到10,在加到10以前,退出這個頁面,就能觀察到上圖所示的警告。函數

import React, { Component} from 'react';
import { 
    View, 
    Text,
} from 'react-native';

export default class Test extends Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0,
        }
    }

    x = 0;

    count = () => {
        if (this.x < 10) {
            this.x++;
            this.setState({count: this.x});
            setTimeout(this.count, 1000);
        }
    }

    componentWillMount() {
        this.count();
    }

    render() {
        return (
            <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
                <Text style={{fontSize: 40}}>{this.state.count}</Text>
            </View>
        )
    }
}

實際上,這個警告能幫助咱們找到程序中的bug,在一個被卸載的組件上調用setState()意味着這個組件可能沒有被正確的清理掉,也就是說,程序仍然具備對這個被卸載組件的引用,這可能致使內存泄漏。flex

之前有一個isMounted()函數用來肯定組件的狀態,避免在被卸載了的組件上調用setState()方法,可是如今已經再也不使用了。this

要解決這個問題,能夠維護一個變量_isMounted,來跟蹤組件的狀態。在componentDidMount()中將其設置爲true,在componentWillUnmount()中將其設置爲false。而後在使用setState, replaceState, or forceUpdate方法時檢查組件被卸載以後是否可能被調用,若是是,則使用_isMounted變量。code

修正後的代碼以下。如今再重複前面的操做,在數字數到10以前退出頁面,就不會出現警告了。component

import React, { Component} from 'react';
import { 
    View, 
    Text,
} from 'react-native';

export default class Test extends Component {
    constructor(props) {
        super(props);
        this.state = {
            count: 0,
        }
    }

    x = 0;

    count = () => {
        if (this.x < 10) {
            this.x++;
            if (this._isMounted) {
                this.setState({count: this.x});
            }
            setTimeout(this.count, 1000);
        }
    }

    _isMounted;

    componentWillMount() {
        this.count();
    }

    componentWillUnmount() {
        this._isMounted = false;
    }

    componentDidMount() {
        this._isMounted = true;
    }

    render() {
        return (
            <View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
                <Text style={{fontSize: 40}}>{this.state.count}</Text>
            </View>
        )
    }
}

無isMounted:

有isMounted:

參考鏈接:

【React報錯】isMounted is an Antipattern

isMounted is an Antipattern

相關文章
相關標籤/搜索