参考
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 被拒绝,则会显示最近错误边界的回退内容。
参数
返回值
use
API 返回从资源中读取的值,例如已解析的 Promise 或 上下文 的解析值。
注意事项
- 必须在组件或 Hook 内调用
use
API。 - 在 服务器组件 中获取数据时,建议使用
async
和await
代替use
。async
和await
从调用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;
}
use
从 if
语句内部调用,允许您有条件地从上下文读取值。
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> ); }
深入探讨
可以使用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:
使用错误边界向用户显示错误
如果希望在 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 对象上调用catch
。catch
接受一个参数:一个函数,该函数接受错误消息作为参数。传递给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);
// ...