use - This feature is available in the latest Canary

Canary 版本

use API 目前仅在 React 的 Canary 和实验性渠道中可用。在此处详细了解 React 的发布渠道

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 钩子不同,use 可以在循环和条件语句(如 if)中调用。与 React 钩子类似,调用 use 的函数必须是组件或钩子。

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

请参阅下面的更多示例。

参数

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

返回值

use API 返回从资源读取的值,例如 Promise 的解决值或 context

注意事项

  • use API 必须在组件或钩子函数内部调用。
  • 服务器组件 中获取数据时,建议使用 asyncawait 而不是 useasyncawait 会从调用 await 的位置继续渲染,而 use 会在数据解析完成后重新渲染组件。
  • 建议在 服务器组件 中创建 Promise 并将它们传递给 客户端组件,而不是在客户端组件中创建 Promise。在客户端组件中创建的 Promise 会在每次渲染时重新创建。从服务器组件传递到客户端组件的 Promise 在重新渲染时是稳定的。请参阅此示例

用法

使用 use 读取上下文

当将 上下文 传递给 use 时,它的工作原理类似于 useContext。虽然 useContext 必须在组件的顶层调用,但 use 可以在条件语句(如 if)和循环语句(如 for)内部调用。建议使用 use 而不是 useContext,因为它更加灵活。

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 作为属性从 服务器组件 传递给 客户端组件 来将数据从服务器流式传输到客户端。

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?

可以将 Promise 从服务器组件传递给客户端组件,并使用use API 在客户端组件中解析。您也可以使用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提供替代值

陷阱

use不能在 try-catch 块中调用。不要使用 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);
// ...