use 是一個 React API,可讓您讀取資源的值,例如 PromiseContext

const value = use(resource);

參考

use(resource)

在您的元件中呼叫 use 來讀取資源的值,例如 PromiseContext

import { use } from 'react';

function MessageComponent({ messagePromise }) {
const message = use(messagePromise);
const theme = use(ThemeContext);
// ...

與 React Hooks 不同,use 可以在迴圈和條件語句(例如 if)中呼叫。與 React Hooks 相似,呼叫 use 的函式必須是元件或 Hook。

當使用 Promise 呼叫時,use API 會與 Suspense錯誤邊界 整合。當傳遞給 use 的 Promise 處於待處理狀態時,呼叫 use 的元件會 _暫停_。如果呼叫 use 的元件被 Suspense 邊界包裝,則會顯示後備內容。一旦 Promise 被解析,Suspense 後備內容將會被使用 use API 返回的資料渲染的元件取代。如果傳遞給 use 的 Promise 被拒絕,則會顯示最近錯誤邊界的後備內容。

請參閱以下更多範例。

參數

  • resource:這是您想要讀取值的資料來源。資源可以是一個 Promise 或一個 context

回傳值

use API 會回傳從資源讀取的值,例如 Promise 的解析值或 context 的值。

注意事項

  • use API 必須在元件或 Hook 中呼叫。
  • 伺服器元件 中擷取資料時,建議使用 asyncawait,而不是 useasyncawait 會從呼叫 await 的位置繼續渲染,而 use 會在資料解析後重新渲染元件。
  • 建議在 伺服器元件 中建立 Promise 並將它們傳遞給 客戶端元件,而不是在客戶端元件中建立 Promise。在客戶端元件中建立的 Promise 會在每次渲染時重新建立。從伺服器元件傳遞到客戶端元件的 Promise 在重新渲染期間是穩定的。請參閱此範例

用法

使用 use 讀取 context

當將 context 傳遞給 use 時,它的工作方式類似於 useContext。雖然 useContext 必須在元件的最上層呼叫,但 use 可以在條件語句(例如 if)和迴圈(例如 for)內呼叫。useuseContext 更受歡迎,因為它更靈活。

import { use } from 'react';

function Button() {
const theme = use(ThemeContext);
// ...

use 會針對您傳遞的 context 回傳 context 值。為了確定 context 值,React 會搜尋元件樹,並找到該特定 context **上方最近的 context provider**。

要將 context 傳遞給 Button,請將它或其父元件之一包裝到對應的 context provider 中。

function MyPage() {
return (
<ThemeContext.Provider value="dark">
<Form />
</ThemeContext.Provider>
);
}

function Form() {
// ... renders buttons inside ...
}

provider 和 Button 之間有多少層元件並不重要。當 Form 內的**任何位置**的 Button 呼叫 use(ThemeContext) 時,它將收到 "dark" 作為值。

useContext 不同,use 可以在條件語句和迴圈(例如 if)中呼叫。

function HorizontalRule({ show }) {
if (show) {
const theme = use(ThemeContext);
return <hr className={theme} />;
}
return false;
}

use 是從 if 語句內呼叫的,允許您有條件地從 Context 讀取值。

陷阱

useContext 一樣,use(context) 總是尋找呼叫它的元件**上方**最近的 context provider。它向上搜尋,而**不會**考慮您呼叫 use(context) 的元件中的 context provider。

import { createContext, use } from 'react';

const ThemeContext = createContext(null);

export default function MyApp() {
  return (
    <ThemeContext.Provider value="dark">
      <Form />
    </ThemeContext.Provider>
  )
}

function Form() {
  return (
    <Panel title="Welcome">
      <Button show={true}>Sign up</Button>
      <Button show={false}>Log in</Button>
    </Panel>
  );
}

function Panel({ title, children }) {
  const theme = use(ThemeContext);
  const className = 'panel-' + theme;
  return (
    <section className={className}>
      <h1>{title}</h1>
      {children}
    </section>
  )
}

function Button({ show, children }) {
  if (show) {
    const theme = use(ThemeContext);
    const className = 'button-' + theme;
    return (
      <button className={className}>
        {children}
      </button>
    );
  }
  return false
}

將資料從伺服器串流到客戶端

可以透過將 Promise 作為 prop 從 伺服器元件 傳遞到 客戶端元件,來將資料從伺服器串流到客戶端。

import { fetchMessage } from './lib.js';
import { Message } from './message.js';

export default function App() {
const messagePromise = fetchMessage();
return (
<Suspense fallback={<p>waiting for message...</p>}>
<Message messagePromise={messagePromise} />
</Suspense>
);
}

客戶端元件 接收到 作為 prop 傳遞的 Promise 後,會將其傳遞給 use API。這允許 客戶端元件伺服器元件最初創建的 Promise 中讀取值。

// message.js
'use client';

import { use } from 'react';

export function Message({ messagePromise }) {
const messageContent = use(messagePromise);
return <p>Here is the message: {messageContent}</p>;
}

因為 MessageSuspense 包裹,所以在 Promise 解析完成之前,會顯示 fallback。當 Promise 解析完成後,use API 會讀取值,Message 元件將會取代 Suspense 的 fallback。

"use client";

import { use, Suspense } from "react";

function Message({ messagePromise }) {
  const messageContent = use(messagePromise);
  return <p>Here is the message: {messageContent}</p>;
}

export function MessageContainer({ messagePromise }) {
  return (
    <Suspense fallback={<p>⌛Downloading message...</p>}>
      <Message messagePromise={messagePromise} />
    </Suspense>
  );
}

注意事項

當將 Promise 從伺服器元件傳遞到客戶端元件時,其解析值必須是可序列化的,才能在伺服器和客戶端之間傳遞。像函數這樣的資料類型是不可序列化的,因此不能作為此類 Promise 的解析值。

深入探討

我應該在伺服器元件還是客戶端元件中解析 Promise?

可以將 Promise 從伺服器元件傳遞到客戶端元件,並在客戶端元件中使用 use API 解析。也可以在伺服器元件中使用 await 解析 Promise,並將所需的資料作為 prop 傳遞給客戶端元件。

export default async function App() {
const messageContent = await fetchMessage();
return <Message messageContent={messageContent} />
}

但在 伺服器元件 中使用 await 會阻塞其渲染,直到 await 陳述式完成。將 Promise 從伺服器元件傳遞到客戶端元件可以防止 Promise 阻塞伺服器元件的渲染。

處理 rejected Promise

在某些情況下,傳遞給 use 的 Promise 可能會被拒絕。您可以透過以下兩種方式處理 rejected Promise:

  1. 使用錯誤邊界向使用者顯示錯誤。
  2. 使用 Promise.catch 提供替代值。

陷阱

無法在 try-catch 區塊中呼叫 use。不要使用 try-catch 區塊,而是 將您的元件包裹在錯誤邊界中,或 使用 Promise 的 .catch 方法提供替代值

使用錯誤邊界向使用者顯示錯誤

如果您希望在 Promise 被拒絕時向使用者顯示錯誤,您可以使用錯誤邊界。要使用錯誤邊界,請將呼叫 use API 的元件包裹在錯誤邊界中。如果傳遞給 use 的 Promise 被拒絕,則會顯示錯誤邊界的 fallback。

"use client";

import { use, Suspense } from "react";
import { ErrorBoundary } from "react-error-boundary";

export function MessageContainer({ messagePromise }) {
  return (
    <ErrorBoundary fallback={<p>⚠️Something went wrong</p>}>
      <Suspense fallback={<p>⌛Downloading message...</p>}>
        <Message messagePromise={messagePromise} />
      </Suspense>
    </ErrorBoundary>
  );
}

function Message({ messagePromise }) {
  const content = use(messagePromise);
  return <p>Here is the message: {content}</p>;
}

使用 Promise.catch 提供替代值

如果您希望在傳遞給 use 的 Promise 被拒絕時提供替代值,您可以使用 Promise 的 catch 方法。

import { Message } from './message.js';

export default function App() {
const messagePromise = new Promise((resolve, reject) => {
reject();
}).catch(() => {
return "no new message found.";
});

return (
<Suspense fallback={<p>waiting for message...</p>}>
<Message messagePromise={messagePromise} />
</Suspense>
);
}

要使用 Promise 的 catch 方法,請在 Promise 物件上呼叫 catchcatch 接受一個參數:一個以錯誤訊息作為參數的函數。傳遞給 catch 的函數 回傳 的任何值都將用作 Promise 的解析值。


疑難排解

「Suspense 異常:這不是一個真正的錯誤!」

您正在 React 組件或 Hook 函式外部調用 use,或者在 try-catch 區塊中調用 use。如果您在 try-catch 區塊中調用 use,請將您的組件包裝在錯誤邊界中,或者調用 Promise 的 catch 方法來捕獲錯誤並使用另一個值來 resolve Promise。參考這些範例

如果您在 React 組件或 Hook 函式外部調用 use,請將 use 的調用移至 React 組件或 Hook 函式內。

function MessageComponent({messagePromise}) {
function download() {
// ❌ the function calling `use` is not a Component or Hook
const message = use(messagePromise);
// ...

請在任何組件閉包外部調用 use,確保調用 use 的函式是組件或 Hook。

function MessageComponent({messagePromise}) {
// ✅ `use` is being called from a component.
const message = use(messagePromise);
// ...