使用 React Suspense 进行流式渲染可以延迟非关键数据加载,从而加快应用的初始渲染速度,并解除 UI 渲染阻塞。
React Router 通过让 loader 和 action 返回 promise 来支持 React Suspense。
React Router 会在渲染路由组件之前等待路由 loader 完成。为了解除非关键数据的 loader 阻塞,请直接从 loader 返回 promise,而不是在 loader 中等待它。
import type { Route } from "./+types/my-route";
export async function loader({}: Route.LoaderArgs) {
// note this is NOT awaited
let nonCriticalData = new Promise((res) =>
setTimeout(() => res("non-critical"), 5000)
);
let criticalData = await new Promise((res) =>
setTimeout(() => res("critical"), 300)
);
return { nonCriticalData, criticalData };
}
promise 将在 loaderData
中可用,<Await>
将等待 promise 完成并触发 <Suspense>
渲染 fallback UI。
import * as React from "react";
import { Await } from "react-router";
// [previous code]
export default function MyComponent({
loaderData,
}: Route.ComponentProps) {
let { criticalData, nonCriticalData } = loaderData;
return (
<div>
<h1>Streaming example</h1>
<h2>Critical data value: {criticalData}</h2>
<React.Suspense fallback={<div>Loading...</div>}>
<Await resolve={nonCriticalData}>
{(value) => <h3>Non critical value: {value}</h3>}
</Await>
</React.Suspense>
</div>
);
}
如果你正在尝试使用 React 19,可以使用 React.use
替代 Await
,但你需要创建一个新组件并将 promise 传递下去以触发 Suspense fallback。
<React.Suspense fallback={<div>Loading...</div>}>
<NonCriticalUI p={nonCriticalData} />
</React.Suspense>
function NonCriticalUI({ p }: { p: Promise<string> }) {
let value = React.use(p);
return <h3>Non critical value {value}</h3>;
}