javapanel根据内部组件_[译]避免在unmounted组件上调用setState
[譯]避免在unmounted組件上調用setState
原文 : https://www.robinwieruch.de/react-warning-cant-call-setstate-on-an-unmounted-component
很多人在開發 React 的過程中,會遇到下面這些警告。github上很多issue都和這些警告相關。因此,我想在這篇文章里,講講下面這兩個警告的原因和應對方法。
- 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.
在大多數情況下,警告并不會讓你的應用崩潰。但你還是應該關注一下這些警告。比如,如果你不能妥善處理你的組件state問題,上面這些警告將會導致性能問題。讓我們進一步來看看這些警告到底是怎么回事。
當你的組件已經unmount之后,如果你還在該組件上調用 this.setState(),那么上面的警告就會出現。有下面幾種情況,都會觸發組件的unmount:
- 根據 React 的條件渲染 ,你不再渲染某個組件了發生了頁面跳轉,比如使用了 React Router,從一個頁面跳轉到了另一個頁面
當組件被unmount之后,你仍然可能會調用該組件的 this.setState() 方法,比如你在該組件內部有異步操作(通常是網絡請求)發生,在異步操作結束之后,你可能需要更新該組件的state。下面是這種情況發生的例子:
- 你在組件內,向某個API接口發起了異步請求,在請求結束之前,你的組件被unmount了。在這之后,請求響應,你需要根據響應的內容,調用組件的 this.setState() 來更新狀態,然而,這時該組件已經被unmount了你在組件內部綁定了一個事件回調函數,但是沒有在組件的 componentWillUnmount 里取消事件綁定。在組件被unmount之后,該事件回調函數可能會被調用你在組件內部有一個定時器(interval),你在計時器的回調函數里,調用了 this.setState()來更新組件狀態。如果你忘記了在 componentWillUnmount 里清除掉定時器,那么就會像上面那樣,在unmount的組件上更新state
出現上面的警告時,最壞的情況是什么呢?它將給你的react應用帶來性能上的負面影響,因為上述情況會導致你的應用存在內存泄露問題。如果你只是在某個組件里,在組件unmount之后還調用了 this.setState ,可能不會有太大的問題。但是,如果你的應用了有很多組件都有這個問題,那你的react應用性能將會收到明顯的的影響。當然了,這也不是最壞的情況。最壞的情況發生在你忘記了取消事件綁定或者清除計時器。想象一下,你在組件內啟動了定時器,每秒都會更新組件的state,之后組件被unmount了。如果你忘記了清除這個定時器,那么你可能會感受到,你的應用性能會被明顯的拖慢。
怎樣避免在定時器/事件回調里調用unmounted組件的setState
你可能已經注意到了,在大多數情況下,如果我們能夠在組件的 componentWillUnmount里正確的處理定時器、事件監聽,那么上面的警告是能夠避免的。比如,在 componentWillUnmount里,清除定時器,取消事件綁定等。
在這個 定時器demo 里,你可以試試不清除掉定時器的情況。
怎樣避免在異步請求里調用unmounted組件的setState
在上面的例子里,我們針對定時器、事件綁定,在 componentWillUnmount里,相應的清除了定時器、取消了事件綁定。實時上,我們沒有理由不這樣做。
那么,在react組件里的異步請求的情況下,該怎么避免上面的問題呢?我們經常會在react組件里發起異步請求,在網絡結束之后,調用 this.setState() 來更新組件state。然而,在我們異步請求結束之前,組件就已經被unmount了,腫么辦?上面的警告會出現在你的瀏覽器控制臺里,因為react不能將state更新到已經unmount的組件上。看個例子:
class News extends Component { constructor(props) { super(props); this.state = { news: [], }; } componentDidMount() { axios .get('https://hn.algolia.com/api/v1/search?query=react') .then(result => this.setState({ news: result.data.hits, }), ); } render() { return (- {this.state.news.map(topic => (
- {topic.title} ))}
要想避免這個問題,你可以在組件unmount的時候,中斷網絡請求,或者在網絡結束時,避免調用 this.setState() 。然而,大多數基于 Promise的網絡請求庫,都沒有提供中斷網絡請求的功能,因此我們需要自己在組件類上,增加一個類的 實例屬性 來標記當前組件是否已經mount了。這個標記默認是 false 的,之后在組件的 componentDidMount里,標記設置為 true;在組件的 componentWillUnmount里,設置為 false。通過這個屬性,我們能夠知道當前組件是否處于mount之后。這個屬性 不會 受 this.setState()的影響,因為它是 實例屬性,我們能夠直接在組件實例上訪問它,不需要經過react組件的 this.state。因為它不在組件的state上,我們修改這個屬性,也就 不會 觸發組件重新render了。看下修改之后的代碼:
class News extends Component { _isMounted = false; constructor(props) { super(props); this.state = { news: [], }; } componentDidMount() { this._isMounted = true; axios .get('https://hn.algolia.com/api/v1/search?query=react') .then(result => { if (this._isMounted) { this.setState({ news: result.data.hits, }); } }); } componentWillUnmount() { this._isMounted = false; } render() { // 省略相同代碼 ... }}現在,即使在組件unmount之后,網絡請求才結束,我們通過 this._isMounted 這個標記,就能避免在網絡請求結束后,調用被unmounted組件的 this.setState()方法。這個demo地址的源碼,在 這個github倉庫 。
通過引入 this._isMounted 這個標記,不會影響到你所使用的網絡請求庫,不管你是使用瀏覽器原生的 fetch 還是 第三方庫比如 axios,都沒有問題。
譯者注:幾年前接觸react的時候,找到的方法,就和文章里的一樣,本來以為會有其他更高大上的東東的……
總結
以上是生活随笔為你收集整理的javapanel根据内部组件_[译]避免在unmounted组件上调用setState的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iPhone 14被看衰:产量比iPho
- 下一篇: bin文件怎么转换成文本文档_怎么把pd