use 是一个 React API,允许您读取资源的值,例如 Promise上下文

const value = use(resource);

参考

use(resource)

在您的组件中调用use以读取资源的值,例如 Promise上下文

import { use } from 'react';

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

与 React Hook 不同,use可以在循环和条件语句(如if)中调用。与 React Hook 一样,调用use的函数必须是组件或 Hook。

当使用 Promise 调用时,use API 与 Suspense错误边界 集成。调用use的组件会在传递给use的 Promise 挂起时挂起。如果调用use的组件包含在 Suspense 边界中,则会显示回退内容。一旦 Promise 完成,Suspense 回退内容将被使用use API 返回的数据渲染的组件替换。如果传递给use的 Promise 被拒绝,则会显示最近错误边界的回退内容。

请参阅下面的更多示例。

参数

  • resource:这是您要从中读取值的数据源。资源可以是 Promise上下文

返回值

use API 返回从资源中读取的值,例如已解析的 Promise上下文 的解析值。

注意事项

  • 必须在组件或 Hook 内调用 use API。
  • 服务器组件 中获取数据时,建议使用 asyncawait 代替 useasyncawait 从调用 await 的位置继续渲染,而 use 会在数据解析后重新渲染组件。
  • 建议在 服务器组件 中创建 Promise 并将其传递给 客户端组件,而不是在客户端组件中创建 Promise。在客户端组件中创建的 Promise 会在每次渲染时重新创建。从服务器组件传递到客户端组件的 Promise 在重新渲染期间保持稳定。参见此示例

用法

使用 use 读取上下文

当将 上下文 传递给 use 时,它的作用类似于 useContext。虽然 useContext 必须在组件的顶层调用,但 use 可以像在 if 语句和 for 循环中那样在条件语句内调用。useuseContext 更灵活,因此更受推荐。

import { use } from 'react';

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

use 返回您传递的 上下文上下文值。为了确定上下文值,React 会搜索组件树并找到该特定上下文的 最近的上下文提供者

要将上下文传递给 Button,请将其或其父组件之一包装到相应的上下文提供者中。

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

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

提供者和 Button 之间有多少层组件并不重要。当 Form 内的任何位置的 Button 调用 use(ThemeContext) 时,它将接收 "dark" 作为值。

useContext 不同,use 可以像在 if 语句中那样在条件语句和循环中调用。

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

useif 语句内部调用,允许您有条件地从上下文读取值。

陷阱

useContext 一样,use(context) 始终查找调用它的组件 上方 的最近的上下文提供者。它向上搜索,并且 不会考虑您从中调用 use(context) 的组件中的上下文提供者。

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>;
}

因为Message 被包装在Suspense 中,所以在 Promise 解析之前将显示回退内容。Promise 解析后,use API 将读取该值,并且Message 组件将替换 Suspense 回退内容。

"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?

可以使用use API 将 Promise 从服务器组件传递到客户端组件并在客户端组件中解析。您也可以使用await 在服务器组件中解析 Promise,并将所需数据作为 prop 传递给客户端组件。

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

但是,在服务器组件中使用await 将阻塞其渲染,直到await 语句完成。将 Promise 从服务器组件传递到客户端组件可以防止 Promise 阻塞服务器组件的渲染。

处理被拒绝的 Promise

在某些情况下,传递给use 的 Promise 可能会被拒绝。您可以通过以下两种方式处理被拒绝的 Promise:

  1. 使用错误边界向用户显示错误。
  2. 使用Promise.catch 提供备用值。

陷阱

不能在 try-catch 块中调用use。不要使用 try-catch 块,而是将您的组件包装在错误边界中,或使用 Promise 的.catch 方法提供备用值

使用错误边界向用户显示错误

如果希望在 Promise 被拒绝时向用户显示错误,可以使用错误边界。要使用错误边界,请将调用use API 的组件包装在错误边界中。如果传递给use 的 Promise 被拒绝,则将显示错误边界的回退内容。

"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 来捕获错误并使用另一个值来解决 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);
// ...