本文轉(zhuǎn)載自我的個(gè)人博客彤守。
這是一篇譯文毯侦,原文在這里。有興趣的同學(xué)可以直接閱讀原文具垫,寫的很好侈离。圖省事的同學(xué)可以直接看我的精簡的譯文。
相信很多react
初學(xué)者都遇到過以下兩個(gè)警告(warnings)
:
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.
Warning: Can’t call setState (or forceUpdate) 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.
一般來說警告并不會(huì)讓你的程序崩潰筝蚕,但是它會(huì)降低程序的性能卦碾,比如我們上面提到的這兩個(gè)警告就是這樣铺坞。下面讓我們來討論一下這兩個(gè)警告到底講的是什么吧。
當(dāng)一個(gè)已經(jīng)被卸載(unmounted)
的組件調(diào)用setState()
方法時(shí)洲胖,你就會(huì)遇到以上兩個(gè)警告济榨。一般來說,有兩種情況會(huì)導(dǎo)致組件的卸載:
- 通過條件渲染的組件绿映,其渲染條件從達(dá)到變?yōu)闆]有達(dá)到擒滑,導(dǎo)致已渲染的組件的卸載
- 通過路由手段路由到別的組件,導(dǎo)致原組件被卸載
當(dāng)組件被卸載后叉弦,被卸載的組件內(nèi)的某些異步邏輯可能在組件被卸載后調(diào)用了setState()
企圖更新組件內(nèi)的state
丐一,通常有三種場景可能會(huì)發(fā)生這種情況:
- 你對(duì)某個(gè)
API
提交了一個(gè)異步請(qǐng)求,組件在收到響應(yīng)前就被卸載了淹冰,收到響應(yīng)之后調(diào)用了setState()
企圖更新state
库车,但這個(gè)時(shí)候組件早就被卸載了。 - 你為組件添加了監(jiān)聽事件樱拴,但是沒有在
componentWillUnmount()
里移除這個(gè)監(jiān)聽事件柠衍,當(dāng)組件移除以后監(jiān)聽的事件被觸發(fā)。 - 你在類似于
setInterval()
的函數(shù)里調(diào)用了setState()
疹鳄,但是沒有在componentWillUnmount()
里移除這些函數(shù)拧略。
那么在遇到以上兩個(gè)警告時(shí)我們怎么樣才能解決他們呢?
避免 intervals/listeners 在已卸載的組件中調(diào)用 setState()
對(duì)于上面提到的三種情況中的后兩種情況瘪弓,我們只需要在componentWillUnmount()
生命周期鉤子里將intervals/listeners
移除就可以了。具體操作可以參考這個(gè)例子禽最。
避免異步請(qǐng)求在已卸載的組件中調(diào)用 setState()
為了避免異步請(qǐng)求在已卸載的組件中調(diào)用setState()
腺怯,我們可以在請(qǐng)求收到響應(yīng)后添加一個(gè)判斷條件,判斷此時(shí)組件有沒有被卸載川无,如果沒有卸載再繼續(xù)執(zhí)行之后的代碼(比如setState()
)呛占。具體實(shí)現(xiàn)如下:
class News extends Component {
//添加一個(gè) class field 記錄組件有沒有被卸載,初始化為false懦趋,表示已卸載
_isMounted = false;
constructor(props) {
super(props);
this.state = {
news: [],
};
}
componentDidMount() {
//組件在被掛載后將_isMounted的值更新為true晾虑,表示已掛載
this._isMounted = true;
axios
.get('https://hn.algolia.com/api/v1/search?query=react')
.then(result => {
//通過_isMounted判斷組件有沒有被卸載
if (this._isMounted) {
this.setState({
news: result.data.hits,
});
}
});
}
componentWillUnmount() {
//在組件被卸載時(shí)將_isMounted更新為false,表示組件已卸載
this._isMounted = false;
}
render() {
...
}
}
一個(gè)小插曲仅叫,關(guān)于class field
是什么帜篇,給大家兩個(gè)例子,可以先體會(huì)下用class field
和不用的區(qū)別诫咱,以后有時(shí)間笙隙,我再寫一篇專門關(guān)于class field
的文章。
不用class field
:
class IncreasingCounter {
constructor() {
this._count = 0;
}
get value() {
console.log('Getting the current value!');
return this._count;
}
increment() {
this._count++;
}
}
用class field
:
class IncreasingCounter {
_count = 0;
get value() {
console.log('Getting the current value!');
return this._count;
}
increment() {
this._count++;
}
}