一探那些令人興奮的 React 18 三大新 features,auto batching,startTransition,Suspense SSR - Jason's Web Memo

一探那些令人興奮的 React 18 三大新 features,auto batching,startTransition,Suspense SSR

React 官方 blog 已經推出 React 18 的新版本的 release 計畫,包含三個主要的新 features,auto batching,start transition,Suspense SSR,本文不會太講細節,只會對目前釋出的訊息做一個精簡的中文摘要,一切以文末的官方文件為主

auto batching

過去 React 就有所謂的 batch update 的機制,如果在

  1. 一次 React synthetic event 中
  2. 在 React synthetic event 中 capture buble phase 中
  3. lifecycle 裡面

同步進行多次 setState,只會用最後的 state 結果觸發一次 rendering,並不會觸發多次

function App() {
const [count, setCount] = useState(0);
const [flag, setFlag] = useState(false);

function handleClick() {
setCount((c) => c + 1); // Does not re-render yet
setFlag((f) => !f); // Does not re-render yet
// React will only re-render once at the end (that's batching!)
}

return (
<div>
<button onClick={handleClick}>Next</button>
<h1 style={flag ? { color: "blue" } : { color: "black" }}>{count}</h1>
</div>
);
}

但在一些情況下,譬如:

  1. setState 放在 setTimeout callback 裡面
  2. setState 放在 feth promise 後面的 .then callback 裡頭
  3. setState 放在 native event 裡頭,而不是 React synthetic event 中

就會導致這樣的 batch 機制就會失效。

而 React 18 之後,所有的 state update 都會 auto batching,即使在上述的狀況下,也不會觸發多次 rendering

setTimeout(() => {
setCount((c) => c + 1);
setFlag((f) => !f);
// React 18 will only re-render once at the end (that's batching!)
}, 1000);
fetch(/*...*/).then(() => {
setCount((c) => c + 1);
setFlag((f) => !f);
// React 18 will only re-render once at the end (that's batching!)
});
elm.addEventListener("click", () => {
setCount((c) => c + 1);
setFlag((f) => !f);
// React 18 will only re-render once at the end (that's batching!)
});

startTransition

React 18 把 state 更新分成兩種

  1. Urgent update
  2. Transition update

Urgent update 指的是直接的互動相關,像是點擊輸入拖拉
Transition update 指的是畫面的轉換,像是從 api 拉資料回傳,或是計算結果等

通常 Urgent update 會跟使用者的體驗有直接的相關,通常比較輕量,是使用者覺得畫面卡不卡順不順的關鍵,而 Transition update,DOM 的更新通常比較大,使用者都可以有一些預期的 delay,過去 React 這兩種 state update 沒有分別,因此輕量 Urgent update 可能會被重型的 Transition update 效能問題給卡住

React 18 新增了一個 startTransition API,可以特別標注 update 為 Transition update,React 而進行 Transition Update 的過程中,可以被 Urgent update 給中斷,React 會拋出尚未完成的 rendering 並只會 render 最新 update 的結果。

import { startTransition } from "react";

// Urgent: Show what was typed
setInputValue(input);

// Mark any state updates inside as transitions
startTransition(() => {
// Transition: Show the results
setSearchQuery(input);
});

Suspense SSR

過去 Server Side Rendering 頁面都會面臨到三個效能上的瓶頸

  1. 必須先拉取所有需要 api 資料才能開始組 html
  2. 必須 load 完所有的 js 才能 hydration
  3. 必須 hydration 所有的 Component 畫面才可以互動

而在 React 18 透過 <Suspense /> 的幫助,原本頁面可以分割成數個 Component,以 Component 作爲單位來進行 streaming Sever Side Rendering 跟 hydration,提升畫面呈現速度跟可互動速度

<Layout>
<NavBar />
<Sidebar />
<RightPane>
<Post />
<Suspense fallback={<Spinner />}>
<Comments />
</Suspense>
</RightPane>
</Layout>

原本的 Suspense 裡頭 Comment Component 等 SSR 好了再 streaming html 跟小 script 送到 browser 進行渲染並替換原本的 spinner

<div hidden id="comments">
<!-- Comments -->
<p>First comment</p>
<p>Second comment</p>
</div>
<script>
// This implementation is slightly simplified
document
.getElementById("sections-spinner")
.replaceChildren(document.getElementById("comments"));
</script>

利用這個替換技巧,原本 renderToStream api 必須跟著 html 結構依序渲染的問題也解決了,誰先 streaming 過來誰就先進行替換

另外透過 React lazy 的 api,可以把原本 script bundle split,而且先準備好的 Component,就可以先 hydration

import { lazy } from "react";

const Comments = lazy(() => import("./Comments.js"));

<Suspense fallback={<Spinner />}>
<Comments />
</Suspense>;

但並非所有的 hydration 都一樣重要,譬如說使用者只想先跟某個模組互動,透過使用者的點擊,React 會調整 hydration 的優先順序,對於使用者關注的模組會優先 hydration

心得結語

React 講了兩三年的 fiber 跟 concurrent rendering 目前看起來即將在 React 18 開花結果,原本厚重的 js call stack 可以 scheduling 後開啟了許多可能性,最令人驚豔的是 Sever Side Rendering 的各種腦洞大開的奇淫技巧,但是好是壞好不好維護有點難以評估,但很符合 facebook 這種很多 widget 小應用介面塞滿整個頁面的 web app 的需求,streaming 或者 Client Side Rendering 的架構對處理 status code 跟 SEO 跟 og img 需求的爬蟲來說仍有些不足,這些問題並沒有隨著 Suspense SSR 而解開,但仍是值得期待。

Ref

  1. the plan for react 18
  2. auto batching
  3. startTransition
  4. Suspense Sever Side Rendering

Webmention 社群迴響 59

喜歡 46
轉推 12
引用或評論 1

用 Webmentions 參與社群迴響

透過 twitter 轉推,你的轉推評論之後會更新在上面社群迴響的引用評論列表。

轉推這篇文章

如果你的 blog 文章想要引用本文,歡迎透過下方表單用將你的 blog 文章網址傳送給我,若你的 blog 文章含有正確的本文網址連結,並且 blog 文章本身支持 microformat,之後你的引用資訊會更新在上面社群迴響的引用評論列表。

社群迴響將不定期更新,不保證同步,同時有資料缺漏的可能性。最新回應請參考以下連結:

Jason Chen - Yahoo Taiwan EC Web frontend engineer currently. Write something about web and React.js here

Jason Chen

Yahoo Taiwan Sr. Frontend Engineer. Write something about web and React.js here.

訂閱 blog 更新 開啟小鈴鐺

複製 RSS xml 網址

訂閱 Google Groups 電子報

追蹤我的 Medium

--

Other Posts