参考
use(resource)
在组件中调用 use
以读取资源的值,例如 Promise 或 context。
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 被拒绝,则将显示最近的错误边界的回退内容。
参数
返回值
use
API 返回从资源读取的值,例如 Promise 的解决值或 context。
注意事项
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 作为属性从 服务器组件 传递给 客户端组件 来将数据从服务器流式传输到客户端。
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 从服务器组件传递给客户端组件,并使用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:
使用错误边界向用户显示错误
如果您希望在 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);
// ...