2162 字
11 分鐘

React 19.3 有哪些值得升級?ViewTransition、Fragment Refs 與 browser() 實作邊界

React 19.3 已在 2026 年 9 月 9 日發布到 npm。這次升級最值得注意的不是「又多幾個 API」,而是三個常被混在一起的問題終於有比較清楚的邊界:View Transition 負責把 transition update 變成動畫、Fragment Refs 負責在不增加 wrapper 的情況下管理一組子節點,而 browser() 負責把無法產生有意義 SSR HTML 的元件交給 client。

直接答案是:如果你需要跨頁或列表的進出場動畫,先評估 <ViewTransition>;如果元件回傳 Fragment 卻仍需要 focus、observer 或 event listener,再評估 Fragment Refs;只有真的無法在 server 產生穩定初始 HTML 時,才使用 use(browser())。三者不是同一種「讓 React 更快」的開關。

React 19.3 的四個重點,先分清用途#

官方這次把穩定功能和 React DOM/Server Components 的更新放在同一篇 release post。實作前可以先用下面的表格縮小範圍:

功能解決的問題不應該拿來做什麼
<ViewTransition>在 Transition、Suspense reveal 或 deferred update 中協調 enter/exit/update/share 動畫不要包住每個小元件,或把 urgent update 硬改成動畫
Fragment Refs對沒有單一 DOM parent 的 Fragment 子節點做 focus、事件或可見度觀察不要把它當成任意 DOM reparent 或繞過元件封裝的通用 ref
use(browser())元件只在瀏覽器有意義,需要 opt out server rendering不要用來掩蓋可修正的 hydration mismatch
Trusted Types support讓 React 傳遞 Trusted Types 物件時不被先轉成字串不會自動建立 sanitizer,也不會代替 CSP policy

這些 API 在 React 19.3 穩定,不代表所有 framework adapter、SSR pipeline 或瀏覽器都已經以相同方式整合。升級時仍要把 React、React DOM、framework、測試 renderer 和 TypeScript 型別一起檢查。

<ViewTransition>:讓有意義的 state transition 產生動畫#

最小用法是把需要協調的畫面包在 <ViewTransition> 裡,並且由 startTransition 標記這次更新:

import { ViewTransition, startTransition, useState } from 'react'
export function DetailsToggle() {
const [showDetails, setShowDetails] = useState(false)
return (
<>
<button
onClick={() => {
startTransition(() => setShowDetails((value) => !value))
}}
>
{showDetails ? '收合' : '展開'}
</button>
{showDetails && (
<ViewTransition>
<Details />
</ViewTransition>
)}
</>
)
}

React 會依 tree 的變化判斷 enter、exit、update 或 share。預設可以先得到 cross-fade,再用 CSS 的 View Transition class 或 onEnteronExitonShareonUpdate 事件細調。要注意的是,沒有被標記為 Transition 的 urgent update 不會因此自動變成動畫;這是保留即時回應的設計。

動畫測試的三個檢查點#

  1. Loading fallback 是否被拖慢。 Suspense fallback 通常應立即出現,已快取的內容也不必每次 reveal 都重新淡入。
  2. Reduced motion 是否有替代樣式。 prefers-reduced-motion: reduce 應停用或縮短非必要動畫。
  3. 狀態改變是否真的屬於 transition。 搜尋、按鈕回饋或表單錯誤等 urgent update 若被包進 transition,使用者可能感覺到延遲。

如果頁面本來就有原生 DOM 的 view-transition-name、複雜的 portal 或第三方動畫庫,請把同一個互動在支援與不支援的瀏覽器各測一次。<ViewTransition> 目前是 DOM 功能,不能直接假設 React Native 有相同支援。

Fragment Refs:沒有 wrapper 也能管理一組子節點#

元件回傳多個 sibling 時,傳統做法往往是加一個 <div> 當 ref anchor;這可能改變 CSS layout、accessibility tree 或 flex/grid 結構。React 19.3 的 Fragment Refs 讓 ref 可以直接放在 <Fragment> 上:

import { Fragment, useEffect, useRef } from 'react'
export function HeadingGroup({ headings }: { headings: string[] }) {
const groupRef = useRef(null)
useEffect(() => {
groupRef.current?.focus()
}, [])
return (
<Fragment ref={groupRef}>
{headings.map((heading) => (
<h2 tabIndex={-1} key={heading}>
{heading}
</h2>
))}
</Fragment>
)
}

這類 ref 提供的是一組子節點的有限操作介面,包括 event listener、focus/blur、IntersectionObserverResizeObserver 以及量測和 scroll。它不是一個虛擬 DOM 節點,也不是把 children 變成可以任意搬動的容器;程式仍要尊重 React 的 reconciliation 和元件生命週期。

實作時特別注意:

  • 要把 ref 放在真正的 Fragment,不是任意 shorthand <>...</>
  • 先確認你需要的是「群組操作」,不是某一個深層 DOM 元素的 ref。
  • observer 需要在 effect 中註冊和清理,避免每次 render 重複連線。
  • 若只為了 focus 一個按鈕,直接讓該按鈕接收 ref 通常更簡單。

如果你的元件是第三方 library,Fragment Refs 可以減少為了暴露單一 wrapper 而修改 API 的需求;但這也表示元件的 DOM 子節點成為行為依賴,升級 library 時要補上 structure-level regression test。

use(browser()):只對真正 browser-only 的元件使用#

Server rendering 的元件應該盡量在 server 和 client 產生相同的初始 HTML。遇到 localStorage、裝置時區、瀏覽器權限或只能在 window 存在時取得的資料,React 19.3 提供 browser() 讓元件主動退出 SSR:

import { Suspense, use } from 'react'
import { browser } from 'react-dom'
function LocalTimeZone() {
use(browser())
const timeZone = new Intl.DateTimeFormat().resolvedOptions().timeZone
return <p>{timeZone}</p>
}
export function TimeZonePanel() {
return (
<Suspense fallback={<p>正在讀取時區……</p>}>
<LocalTimeZone />
</Suspense>
)
}

server render 時,最近的 Suspense fallback 會進到 HTML;元件 hydrate 後,use(browser()) 不會在 client suspend,元件才繼續讀取 browser-only API。這個模型比在 useEffect 裡設一個 mounted state 更直接,但代價是該區塊的 server HTML 只有 fallback,可能影響 SEO、首屏內容和無 JavaScript 的可用性。

可以用這個決策順序:

  1. 能不能由 server 傳入固定的 initialValue?可以的話,優先用它,保留穩定初始 HTML。
  2. 能不能把 browser-only 讀取延後到互動發生時?可以的話,不要讓整個元件退出 SSR。
  3. 只有在沒有合理 server fallback 時,才使用 use(browser()),並替 Suspense fallback 寫好 loading、accessibility 和 layout 保留。

不要只把 typeof window !== 'undefined' 換成 browser() 就算完成;兩者的目的都是處理環境差異,但 browser() 會和 Suspense、SSR streaming 與 hydration 一起工作,驗收範圍更大。

Trusted Types:React 支援不等於網站已經安全#

React 19.3 會在 Trusted Types 物件傳到 DOM injection sink 前保留它,而不是先用字串串接把 TrustedHTMLTrustedScriptTrustedScriptURL 轉回普通字串。要發揮效果,網站仍需要瀏覽器 CSP 的 require-trusted-types-for 'script' 和可信任的 sanitizer policy。

升級後如果要啟用,請分三層驗證:

層級要確認什麼
React 元件dangerouslySetInnerHTML、script URL 和第三方 widget 使用的值來源
sanitizer policy只有經過允許的清理流程才建立 Trusted Types 物件
CSP/部署report-only 觀察期、正式 enforcement、legacy browser fallback

不要因為 React release note 寫了 Trusted Types support,就把現有的 HTML sanitize 移除。React 只改善值在 DOM API 間的傳遞方式,內容安全仍由你的 policy、來源驗證和 CSP 負責。

升級驗收:一次只導入一種新能力#

React 19.3 升級建議分階段做:

Terminal window
pnpm why react react-dom
pnpm exec tsc --noEmit
pnpm test
pnpm build

先只升級 reactreact-dom 和對應型別,確認 hydration、Strict Mode、Suspense、router 與 test renderer 沒有回歸;再以小型元件導入 <ViewTransition>;最後才在確定 server fallback 可接受的區塊使用 browser()。若你原本就直接使用 DOM state-preserving API,也可以參考 moveBefore() 的狀態邊界;它和 React reconciliation 一樣,都要求先確認「誰擁有這個 DOM」。

CI 至少加上以下情境:

  • SSR 輸出的 HTML 與 client 第一次 render 是否符合預期。
  • transition 中的 fallback、錯誤邊界和取消更新。
  • Fragment Ref 的 focus、observer cleanup 與無 wrapper layout。
  • prefers-reduced-motion 和不支援 View Transition 的瀏覽器。
  • Trusted Types report-only 與正式 CSP 的 console/response header。

結論:穩定 API 不代表可以跳過邊界設計#

React 19.3 的價值,是把常見的 UI 需求放進更清楚的 primitives:動畫交給 <ViewTransition>,群組 DOM 行為交給 Fragment Refs,browser-only rendering 交給 use(browser()),Trusted Types 則改善安全 API 的相容性。升級時先選一個真實問題導入,為它補上 SSR、fallback、accessibility 和 reduced-motion 測試,再擴大範圍,比一次把所有新 API 套進元件庫更容易定位回歸。

常見問題#

Q: <ViewTransition> 包住元件後,任何 state 更新都會動畫嗎?#

A: 不會。React 主要對 Transition、Suspense reveal 或 deferred value 造成的更新協調 View Transition;urgent update 不會因為被包住就自動變成動畫。動畫應該服務狀態變化的語意,不是把所有 render 都變慢。

Q: Fragment Refs 可以取代所有 DOM ref 嗎?#

A: 不行。它適合一組 Fragment 子節點的 focus、事件、觀察和量測;若你只需要操作一個具名元素,直接把一般 ref 傳給該元素更清楚。Fragment Refs 也不代表可以繞過 React 隨意搬動或重排 DOM。

Q: 使用 use(browser()) 會不會讓 SEO 消失?#

A: 這個元件在 server 會以 Suspense fallback 呈現,因此原本由元件輸出的內容可能不會出現在初始 HTML。需要被搜尋引擎理解的標題、描述和主要內容,應優先由 server 產出;browser-only 元件請提供有意義的 fallback 或 server initialValue

參考資料:

React 19.3 release post

React <ViewTransition> reference

React <Fragment> reference

React 19.3 有哪些值得升級?ViewTransition、Fragment Refs 與 browser() 實作邊界
https://laplusda.com/posts/react-19-3-view-transition-fragment-ref/
作者
Zero
發佈於
2026-09-16
許可協議
CC BY-NC-SA 4.0
這篇文章有幫助嗎?

回報錯字、失效連結,或告訴我你想看的延伸主題。