React的并發模式該如何使用?
譯文【51CTO.com快譯】本文將向您介紹React并發模式背后的思想,以及它的一些用法和優點。React的并發模式是一組創新的特性,旨在改進異步呈現的處理。這些改進帶來了更好的終端用戶體驗。
如何讓Web客戶端呈現異步更新是業內一直面臨的問題。React團隊通過向React 16.x中添加并發模式支持,延續了將優秀解決方案引入框架的傳統。
在很多情況下,對狀態變化的幼稚呈現會導致不太理想的行為,例如乏味的加載屏幕、不穩定的輸入處理和不必要的旋轉。
零零碎碎地解決這些問題是容易出錯的。React的并發模式代表了一種全面的,框架化的解決方案。其核心思想是:React在內存中并發地繪制更新,支持可中斷的渲染,并提供了應用程序代碼與這種支持交互的方式。
在React中啟用并發模式
用于利用這些功能的API仍在不斷變化,你必須明確地安裝它,如下所示:
npm install react @ experimental react - dom @ experimental
并發模式是對React工作方式的全局更改,并要求根節點通過并發引擎傳遞。這是通過在應用的根目錄上調用createRoot來完成的,而不是僅僅調用reactDOM.render()。如Listing 1所示。
Listing 1. Using the concurrent renderer
ReactDOM.createRoot(
document.getElementById('root')
).render(<App />);
需要注意的是,createRoot只有在安裝了實驗包之后才可用。而且因為它是一個根本性的變化,現有的代碼庫和庫可能與它不兼容。特別是現在用UNSAFE_作為前綴的生命周期方法是不兼容的。
正因為如此,React在我們現在使用的老式渲染引擎和并發模式之間引入了一個中間步驟。這個步驟稱為“阻塞模式”,它向后兼容,但并發特性更少。
從長遠來看,并發模式將成為默認模式。在中期,React將支持以下三種模式:
1. 舊版模式:ReactDOM.render(<App />, rootNode)?,F有的舊版模式。
2. 封鎖模式:ReactDOM.createBlockingRoot(rootNode).render(<App />)。更少的改變,更少的特性。
3. 并發模式:ReactDOM.createRoot(rootNode).render(<App />)。完全并發模式,具有許多重大的更改。
React中的一個新的渲染模型
并發模式從根本上改變了React渲染接口的方式,以允許在獲取數據的過程中渲染接口。這意味著React必須知道一些關于組件的信息。具體來說,React現在必須知道組件的數據獲取狀態。
React的新Suspense組件
新Suspense組件是最突出的功能,您可以使用此組件通知React UI的給定區域依賴于異步數據加載,并向其提供此類加載的狀態。
這個功能是在框架級別上執行的,這意味著您的數據獲取庫必須通過實現Suspense API來警告React的狀態。目前,Relay為GraphQL執行此操作,而react- suspend -fetch項目處理REST數據的獲取。
重申一下,現在需要使用更智能的數據獲取庫,該庫能夠告訴React它的狀態是什么,從而允許React優化UI的呈現方式。如下Listing 2包含了重要的視圖模板細節。
Listing 2. Using Suspense in the view
<Suspense fallback={<h1>Loading profile...</h1>}>
<ProfileDetails />
<Suspense fallback={<h1>Loading posts...</h1>}>
<ProfileTimeline />
</Suspense>
</Suspense>
注意,這 Suspense 允許定義備用加載內容。這類似于您如何根據舊渲染引擎中組件的加載狀態在組件內部使用不同的返回值來渲染占位符,直到數據準備就緒。
在這個視圖模板使用的組件中,不需要任何特殊代碼即可處理加載狀態?,F在,所有這些都由框架和數據提取庫在后臺處理。
例如,ProfileDetails組件可以加載它的數據并返回它的標記,如Listing 3所示。同樣,這取決于resource實現Suspense API的數據存儲(在Listing 3中是resource對象)。
Listing 3. ProfileDetails
function ProfileDetails() {
const user = resource.user.read();
return <h1>{user.name}</h1>;
}
數據獲取中的并發性
這種設置的一個重要好處是,所有的數據獲取都可以并發進行。因此,你的UI既受益于改進的渲染生命周期,也受益于為多個組件實現并行數據獲取的簡單而自動的方法。
React的useTransition
新的concurrent React工具包中的下一個主要工具是useTransition。這是一個更細粒度的工具,允許你調整UI轉換的發生方式。Listing 4給出了一個使用useTransition包裝轉換的示例。
Listing 4. useTransition
function App() {
const [resource, setResource] = useState(initialResource);
const [ startTransition, isPending ] = useTransition({ timeoutMs: 3000 });
return (
<>
<button
onClick={() => {
startTransition(() => {
const nextUserId = getNextId(resource.userId);
setResource(fetchProfileData(nextUserId));
});
}}> Next </button>
{isPending ? " Loading..." : null}
<ProfilePage resource={resource} />
</>
);
}
Listing 4中的代碼表示,“將新狀態的顯示延遲到三秒?!边@段代碼之所以能夠工作,是因為ProfilePage在使用時是由Suspense組件包裝的。React開始獲取數據,并且不顯示占位符,而是將現有內容顯示已定義的時間timeoutMs。提取完成后,React將顯示更新的內容。這是一種用于改善過渡效果的簡單機制。
useTransition公開的startTransition函數允許你包裝代碼的獲取部分,而isPending函數公開一個布爾標志,你可以使用它來處理條件加載顯示。
所有這一切之所以成為可能,是因為React的并發模式實現了一種后臺呈現機制:當獲取發生時,React會在后臺的內存中呈現更新后的狀態UI。
React的useDeferredValue
最后一個例子涉及修復類型導致數據加載時不一致的類型問題。這是一個典型的問題,通常通過輸入的反彈/節流來解決。并發模式提供了一個更一致、更流暢的解決方案:useDeferredValue。
舉個例子。這個解決方案的天才之處在于它能讓你兩全其美。輸入保持響應狀態,并且列表在數據可用時立即更新。
Listing 5. useDeferredValue in action
const [text, setText] = useState("hello");
const deferredText = useDeferredValue(text, { timeoutMs: 5000 });
// ....
<MySlowList text={deferredText} />
與我們使用useTransition包裝轉換的方式類似,我們使用useDeferredValue包裝資源值。這允許值在timeoutMs期間保持不變。管理此改進的渲染的所有復雜性均由React和數據存儲在后臺處理。
使用Suspense和并發模式的另一個好處是,避免了在生命周期和方法中手動加載數據而引入的競爭條件。保證數據到達并按照請求的順序應用。因此,新模式避免了由于請求響應的交錯而手工檢查數據過時性的需要。
這些是新并發模式的一些亮點。它們提供了令人信服的好處,這將成為未來的常態。
【51CTO譯稿,合作站點轉載請注明原文譯者和出處為51CTO.com】