参考
🌐 Reference
use(resource)
在你的组件中调用 use 来读取像 Promise 或 context 这样的资源的值。
🌐 Call use in your component to read the value of a resource like a Promise or context.
import { use } from 'react';
function MessageComponent({ messagePromise }) {
const message = use(messagePromise);
const theme = use(ThemeContext);
// ...与 React Hooks 不同,use 可以像 if 一样在循环和条件语句中调用。像 React Hooks 一样,调用 use 的函数必须是一个组件或 Hook。
🌐 Unlike React Hooks, use can be called within loops and conditional statements like if. Like React Hooks, the function that calls use must be a Component or Hook.
当使用 Promise 调用时,use API 会与 Suspense 和 错误边界 集成。调用 use 的组件在传递给 use 的 Promise 处于等待状态时会 挂起。如果调用 use 的组件被封装在 Suspense 边界中,将会显示备用内容。一旦 Promise 被解决,Suspense 的备用内容会被使用 use API 返回的数据渲染的组件替换。如果传递给 use 的 Promise 被拒绝,则会显示最近的错误边界的备用内容。
🌐 When called with a Promise, the use API integrates with Suspense and Error Boundaries. The component calling use suspends while the Promise passed to use is pending. If the component that calls use is wrapped in a Suspense boundary, the fallback will be displayed. Once the Promise is resolved, the Suspense fallback is replaced by the rendered components using the data returned by the use API. If the Promise passed to use is rejected, the fallback of the nearest Error Boundary will be displayed.
参数
🌐 Parameters
返回
🌐 Returns
use API 返回从资源中读取的值,就像 Promise 或 context 的解析值一样。
🌐 The use API returns the value that was read from the resource like the resolved value of a Promise or context.
注意事项
🌐 Caveats
useAPI 必须在组件或 Hook 内部调用。- 在 服务器组件 中获取数据时,优先使用
async和await而非use。async和await从调用await的点开始接管渲染,而use会在数据解析后重新渲染组件。 - 更倾向于在 服务器组件 中创建 Promise,并将它们传递给 客户端组件,而不是在客户端组件中创建 Promise。在客户端组件中创建的 Promise 会在每次渲染时重新创建。从服务器组件传递到客户端组件的 Promise 在重新渲染时保持稳定。参见此示例。
用法
🌐 Usage
正在阅读带有 use 的上下文
🌐 Reading context with use
当一个 context 被传递给 use 时,它的工作方式类似于 useContext。虽然 useContext 必须在组件的顶层调用,但 use 可以在像 if 这样的条件语句和像 for 这样的循环中调用。相比 useContext,更推荐使用 use,因为它更灵活。
🌐 When a context is passed to use, it works similarly to useContext. While useContext must be called at the top level of your component, use can be called inside conditionals like if and loops like for. use is preferred over useContext because it is more flexible.
import { use } from 'react';
function Button() {
const theme = use(ThemeContext);
// ...use 返回你传入的 <CodeStep step={1}>context</CodeStep> 的 <CodeStep step={2}>context value</CodeStep>。为了确定上下文值,React 会搜索组件树,并找到该特定上下文上方最近的上下文提供者。
要将上下文传递给 Button,请将其或其某个父组件封装到相应的上下文提供者中。
🌐 To pass context to a Button, wrap it or one of its parent components into the corresponding context provider.
function MyPage() {
return (
<ThemeContext value="dark">
<Form />
</ThemeContext>
);
}
function Form() {
// ... renders buttons inside ...
}无论提供者和 Button 之间有多少层组件。当 Form 中的任何地方的 Button 调用 use(ThemeContext) 时,它将收到 "dark" 作为值。
🌐 It doesn’t matter how many layers of components there are between the provider and the Button. When a Button anywhere inside of Form calls use(ThemeContext), it will receive "dark" as the value.
与useContext不同, use 可以像 if一样在条件语句和循环中被调用。
🌐 Unlike useContext, use can be called in conditionals and loops like 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 value="dark"> <Form /> </ThemeContext> ) } function Form() { return ( <Panel title="欢迎"> <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 }
将数据从服务器流式传输到客户端
🌐 Streaming data from the server to the client
可以通过将一个 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>
);
}然后, 客户端组件 会将 它作为属性接收到的 Promise 传递给 use 接口。这允许 客户端组件 从最初由服务器组件创建的 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>;
}Because Message is wrapped in Suspense, the fallback will be displayed until the Promise is resolved. When the Promise is resolved, the value will be read by the use API and the Message component will replace the 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> ); }
深入研究
🌐 Should I resolve a Promise in a Server or Client Component?
一个 Promise 可以从服务器组件传递到客户端组件,并在客户端组件中使用 use API 进行解析。你也可以在服务器组件中使用 await 解析 Promise,并将所需数据作为 prop 传递给客户端组件。
🌐 A Promise can be passed from a Server Component to a Client Component and resolved in the Client Component with the use API. You can also resolve the Promise in a Server Component with await and pass the required data to the Client Component as a prop.
export default async function App() {
const messageContent = await fetchMessage();
return <Message messageContent={messageContent} />
}但是在服务器组件中使用await会阻止其渲染,直到await语句完成。从服务器组件向客户端组件传递 Promise 会防止该 Promise 阻塞服务器组件的渲染。
🌐 But using await in a Server Component will block its rendering until the await statement is finished. Passing a Promise from a Server Component to a Client Component prevents the Promise from blocking the rendering of the Server Component.
处理被拒绝的 Promise
🌐 Dealing with rejected Promises
在某些情况下,传递给 use 的 Promise 可能会被拒绝。你可以通过以下方式处理被拒绝的 Promise:
🌐 In some cases a Promise passed to use could be rejected. You can handle rejected Promises by either:
使用错误边界向用户显示错误
🌐 Displaying an error to users with an Error Boundary
如果你想在 Promise 被拒绝时向用户显示错误,可以使用 错误边界。要使用错误边界,请将调用 use API 的组件封装在错误边界中。如果传递给 use 的 Promise 被拒绝,错误边界的备用内容将会显示。
🌐 If you’d like to display an error to your users when a Promise is rejected, you can use an Error Boundary. To use an Error Boundary, wrap the component where you are calling the use API in an Error Boundary. If the Promise passed to use is rejected the fallback for the Error Boundary will be displayed.
"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 提供一个备用值
🌐 Providing an alternative value with 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 的解析值。
故障排除
🌐 Troubleshooting
Suspense异常:这不是真正的错误!
🌐 “Suspense Exception: This is not a real error!”
你要么在 React 组件或 Hook 函数外调用 use,要么在 try–catch 块中调用 use。如果你在 try–catch 块中调用 use,请将你的组件封装在错误边界中,或调用 Promise 的 catch 来捕获错误并用另一个值解析 Promise。参见这些例子。
🌐 You are either calling use outside of a React Component or Hook function, or calling use in a try–catch block. If you are calling use inside a try–catch block, wrap your component in an Error Boundary, or call the Promise’s catch to catch the error and resolve the Promise with another value. See these examples.
如果你在 React 组件或 Hook 函数之外调用 use,请将 use 调用移到 React 组件或 Hook 函数中。
🌐 If you are calling use outside a React Component or Hook function, move the use call to a React Component or Hook function.
function MessageComponent({messagePromise}) {
function download() {
// ❌ the function calling `use` is not a Component or Hook
const message = use(messagePromise);
// ...相反,请在任何组件闭包之外调用 use,其中调用 use 的函数是一个组件或 Hook。
🌐 Instead, call use outside any component closures, where the function that calls use is a Component or Hook.
function MessageComponent({messagePromise}) {
// ✅ `use` is being called from a component.
const message = use(messagePromise);
// ...