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


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

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.

通常我們會在componentWillMount方法中執行異步數據請求,然后調用setState方法處理得到的數據來更新界面。有時會遇到這個警告,如下圖:

警告提示我們可能在被卸載的組件上調用了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()意味着這個組件可能沒有被正確的清理掉,也就是說,程序仍然具有對這個被卸載組件的引用,這可能導致內存泄漏。

以前有一個isMounted()函數用來確定組件的狀態,避免在被卸載了的組件上調用setState()方法,但是現在已經不再使用了。

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

修正后的代碼如下。現在再重復前面的操作,在數字數到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++;
            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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM