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:
參考鏈接: