當前位置:
首頁 > 科技 > React中setState 為什麼是非同步的?

React中setState 為什麼是非同步的?

不知道大家有沒有過這個疑問,React 中為什麼是非同步的?很多人一度認為是同步的,知道它是非同步的之後很是困惑,甚至期待 React 能出一個之類的 API。同樣有此疑問的還有 MobX 的作者Michel Weststrate,他認為經常聽到的答案都很容易反駁,並認為這可能是一個歷史包袱,所以開了一個issue詢問真正的原因。最終這個 issue 得到了 React 核心成員Dan Abramov的回復,Dan 的回復表明這不是一個歷史包袱,而是一個經過深思熟慮的設計。

注意:這篇文章根據 Dan 的回復總結分析。

Dan 在回復中表示為什麼是非同步的,這並沒有一個明顯的答案(obvious answer),每種方案都有它的權衡。但是 React 的設計有以下幾點考量:

一、保證內部的一致性

首先,我想我們都同意推遲並批量處理重渲染是有益而且對性能優化很重要的,無論是同步的還是非同步的。那麼就算讓同步更新,也不行,因為當父組件重渲染(re-render )了你才知道。

現在的設計保證了 React 提供的 objects(state,props,refs)的行為和表現都是一致的。為什麼這很重要?Dan 舉了個栗子:

假設是同步更新的,那麼下面的代碼是可以按預期工作的:

然而,這時你需要將狀態提升到父組件,以供多個兄弟組件共享:

需要指出的是,在 React 應用中這是一個很常見的重構,幾乎每天都會發生。

然而下面的代碼卻不能按預期工作:

這是因為同步模型中,雖然會立即更新,但是並不會。而且在沒有重渲染父組件的情況下,我們不能立即更新。如果要立即更新(也就是立即重渲染父組件),就必須放棄批處理(根據情況的不同,性能可能會有顯著的下降)。

所以為了解決這樣的問題,在 React 中和都是非同步更新的,在上面的例子中重構前跟重構後都會列印出 0。這會讓狀態提升更安全。

最後 Dan 總結說,React 模型更願意保證內部的一致性和狀態提升的安全性,而不總是追求代碼的簡潔性。

二、性能優化

我們通常認為狀態更新會按照既定順序被應用,無論是同步更新還是非同步更新。然而事實並不一定如此。

React 會依據不同的調用源,給不同的調用分配不同的優先順序。調用源包括事件處理、網路請求、動畫等。

Dan 又舉了個栗子。假設你在一個聊天窗口,你正在輸入消息,組件中的調用需要被立即應用。然而,在你輸入過程中又收到了一條新消息。更好的處理方式或許是延遲渲染新的組件,從而讓你的輸入更加順暢,而不是立即渲染新的組件阻塞線程,導致你輸入抖動和延遲。

如果給某些更新分配低優先順序,那麼就可以把它們的渲染分拆為幾個毫秒的塊,用戶也不會注意到。

三、更多的可能性

Dan 最後說到,非同步更新並不只關於性能優化,而是 React 組件模型能做什麼的一個根本性轉變(fundamental shift)。

Dan 還是舉了個栗子。假設你從一個頁面導航到到另一個頁面,通常你需要展示一個載入動畫,等待新頁面的渲染。但是如果導航非常快,閃爍一下載入動畫又會降低用戶體驗。

如果這樣會不會好點,你只需要簡單的調用去渲染一個新的頁面,React 「在幕後」開始渲染這個新的頁面。想像一下,不需要你寫任何的協調代碼,如果這個更新花了比較長的時間,你可以展示一個載入動畫,否則在新頁面準備好後,讓 React 執行一個無縫的切換。此外,在等待過程中,舊的頁面依然可以交互,但是如果花費的時間比較長,你必須展示一個載入動畫。

事實證明,在現在的 React 模型基礎上做一些生命周期調整,真的可以實現這種設想。@acdlite 已經為這個功能努力幾周了,並且很快會發布一個 RFC(亦可賽艇!)。

需要注意的是,非同步更新是有可能實現這種設想的前提。如果同步更新就沒有辦法在幕後渲染新的頁面,還保持舊的頁面可以交互。它們之間獨立的狀態更新會衝突。

Dan 最後對 Michel 說到:我希望我們能在接下來幾個月說服你,並且你會欣賞到 React 模型的靈活性。

據我理解,這種靈活性至少一部分要歸功於的非同步更新。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 WEB開發李家靖 的精彩文章:

JS 數組循環遍歷方法的對比

TAG:WEB開發李家靖 |