普通文組 2.5

React 19 筆記:新 api 與 pros 功能篇

提醒:此篇筆記整理了 React 19 Beta 中關於 api 與 props 的改動,但不是一份 100% 詳盡的翻譯與解說文,我只記錄了自認需要關注的內容。如果你想了解 React 19 預計要提供的所有改動,請務必閱讀官方文件。

use

可直接讀取 Promise / React context 的內容,但無法讀取在渲染時產生的 Promise(does not support promises created in render)。

讀取 Promise:

import { use } from "react";
function Comments({ commentsPromise }) {
// `use` will suspend until the promise resolves.
const comments = use(commentsPromise);
return comments.map((comment) => <p key={comment.id}>{comment}</p>);
}
function Page({ commentsPromise }) {
// When `use` suspends in Comments,
// this Suspense boundary will be shown.
return (
<Suspense fallback={<div>Loading...</div>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
);
}

讀取 Context:

import { use } from "react";
import ThemeContext from "./ThemeContext";
function Heading({ children }) {
if (children == null) {
return null;
}
// This would not work with useContext
// because of the early return.
const theme = use(ThemeContext);
return <h1 style={{ color: theme.color }}>{children}</h1>;
}

注意事項:

  • 此 api 可以根據條件決定是否被呼叫(use can be called conditionally),也可以在迴圈中使用
  • 此 api 只能在 hook 或元件中被呼叫
  • 在透過 use 讀取 Promise 內容時,需注意該 Promise 如何處理錯誤情境。會拋錯的話,可以搭配套件 react-error-boundaryErrorBoundary 來顯示錯誤訊息。也可考慮對 Promise 加上 .catch() 來應對,參考官方範例

ref 相關改動

可直接透過 props 傳遞

在 React 19 後,可直接透過 props 傳遞 ref,不需要再搭配 forwardRef 了。且未來 forwardRef 會被標記為待移除(we will deprecate and remove forwardRef)。

// before
import { forwardRef } from "react";
const MyInput = forwardRef(function MyInput(props, ref) {
return <input placeholder={placeholder} ref={ref} />;
});
// after
function MyInput({ placeholder, ref }) {
return <input placeholder={placeholder} ref={ref} />;
}

可執行 cleanup function

當元件要從畫面上被卸除時,React 會執行 ref 中的被回傳的 cleanup 功能。如以下範例:

<input
ref={(ref) => {
// do something when ref created
return () => {
// do something when component unmounts
};
}}
/>

<Context.Provider> 簡化為 <Context>

如標題以及以下範例所示,語法從原本的 <ThemeContext.Provider> 簡化為 <ThemeContext>

const ThemeContext = createContext("");
function App({ children }) {
return <ThemeContext value="dark">{children}</ThemeContext>;
}

支援文件 metadata

當 React 渲染包含 metadata 的元件時,這些 meta tag 會被提升(hoist)至 HTML 文件的 <head> 中。

function BlogPost({ post }) {
return (
<article>
<h1>{post.title}</h1>
<title>{post.title}</title>
{/* support document metadata */}
<meta name="author" content="Josh" />
<link rel="author" href="https://twitter.com/joshcstory/" />
<meta name="keywords" content={post.keywords} />
{...}
</article>
);
}

透過參數 precedence 可指定樣式的載入順序。比如以下範例中的樣式,在 HTML 文件中的順序從上到下會是 foo >> baz >> bar

function ComponentOne() {
return (
<Suspense fallback="loading...">
<link rel="stylesheet" href="foo" precedence="default" />
<link rel="stylesheet" href="bar" precedence="high" />
<article class="foo-class bar-class"></article>
{...}
</Suspense>
);
}
function ComponentTwo() {
return (
<div>
<p>{...}</p>
<link rel="stylesheet" href="baz" precedence="default" />
</div>
);
}

另外,即使在整個 App 多處重複載入帶有樣式表的元件,該樣式表在 HTML 文件中也只會出現一次(React will only include the stylesheet once in the document)。

<script> 支援 async

邏輯與上方透過 <link> 載入樣式類似——一個包含 <script> 的元件即使在整個 App 中出現多次,React 也只會在 HTML 中嵌入一次標籤。

支援外部資源預載

react-dom 追加數種可以預先載入資源的 api:

import { preconnect, prefetchDNS, preinit, preload } from "react-dom";
function MyComponent() {
preinit("https://.../path/to/some/script.js", { as: "script" }); // loads and executes this script eagerly
preload("https://.../path/to/font.woff", { as: "font" }); // preloads this font
preload("https://.../path/to/stylesheet.css", { as: "style" }); // preloads this stylesheet
prefetchDNS("https://..."); // when you may not actually request anything from this host
preconnect("https://..."); // when you will request something but aren't sure what
}

上方元件最終會渲染出以下結果:

<!-- the above would result in the following DOM/HTML -->
<html>
<head>
<!-- links/scripts are prioritized by their utility to early loading, not call order -->
<link rel="prefetch-dns" href="https://..." />
<link rel="preconnect" href="https://..." />
<link rel="preload" as="font" href="https://.../path/to/font.woff" />
<link rel="preload" as="style" href="https://.../path/to/stylesheet.css" />
<script async="" src="https://.../path/to/some/script.js"></script>
</head>
<body>
...
</body>
</html>

注意事項:外部資源實際渲染出來的順序是根據其「功能」而非「在元件中被呼叫的順序」決定。

參考文件