新闻详情

新闻详情

首页 / 资讯中心 / 详情

TanStack Router 的 Await 组件:在 React 18 中渲染 Deferred 数据的 Suspense 方案

发布时间:2026/9/14 1:37:26来源:尧图网络
TanStack Router 的 Await 组件:在 React 18 中渲染 Deferred 数据的 Suspense 方案
TanStack Router 的 Await 组件在 React 18 中渲染 Deferred 数据的 Suspense 方案【免费下载链接】router A client-first, server-capable, fully type-safe router and full-stack framework for the web (React and more).项目地址: https://gitcode.com/GitHub_Trending/ro/routerAwait组件是 TanStack RouterReact 版提供的 Suspense 原语它让组件挂起suspend在某个 Promise 上直到该 Promise 解决或拒绝再把解决值作为参数传给children渲染函数。它是 React 18 环境下配合defer加载延迟数据的标准方案如果你已使用 React 19可以直接改用use()Hook本文会同时覆盖两者。读完本文你将掌握Await的完整 props 契约、三种返回状态的行为定义以及它在源码层面如何通过React.use与TSR_DEFERRED_PROMISE状态机实现挂起/恢复从而在真实项目里正确渲染 loader 返回的延迟数据。核心定位React 18 下的挂起组件官方文档对Await的定义非常明确TheAwaitcomponent is a component that suspends until the provided promise is resolved or rejected. This is only necessary for React 18. If you are using React 19, you can use theuse()hook instead.Await组件会挂起直到传入的 promise 解决或拒绝。这仅在 React 18 中有必要使用 React 19 时可以直接使用use()Hook。这个React 18 only的限定不是营销话术而是由源码直接决定的。在 awaited.tsx 中useAwaited的逻辑是一个双分支结构export function useAwaitedT({ promise: _promise }: AwaitOptionsT): T { if (reactUse) { const data reactUse(_promise) return data } const promise defer(_promise) if (promise[TSR_DEFERRED_PROMISE].status pending) { throw promise } if (promise[TSR_DEFERRED_PROMISE].status error) { throw promise[TSR_DEFERRED_PROMISE].error } return promise[TSR_DEFERRED_PROMISE].data }第一个分支如果运行时存在React.useReact 19直接把 Promise 交给原生use()处理React 自身的 Suspense 机制接管挂起与恢复第二个分支React 18由于React.use不存在组件改用手动挂起策略——通过defer给 Promise 附加内部状态在状态为pending时主动throw promise让上层Suspense显示 fallback状态为error时抛出错误交给 error boundary 处理否则直接返回data。这解释了文档中only necessary for React 18的准确含义Await在 React 19 下依然可用但挂起逻辑会退化为对React.use的一层薄封装真正手动投掷 Promise的机制只服务于 React 18。一个兼容性细节动态查找 React.useutils.ts 中有一处值得注意的实现// Safe version of React.use that will not cause compilation errors against // React 18 with Webpack, which statically analyzes imports and fails when it // sees React.use referenced (since use is not exported from React 18). let REACT_USE use export const reactUse: | (T(usable: PromiseT | React.ContextT) T) | undefined (React as any)[REACT_USE]注释说明了动机React 18 Webpack 环境下静态引用React.use会因 React 18 未导出该属性而在编译期报错因此这里用字符串键动态查找并刻意用let防止打包器常量折叠。如果你在项目里也做过类似的 React 18/19 双版本兼容这段代码是一个可参考的处理模式。Await 的 props 契约props.promise必选类型PromiseT要等待的 Promise。通常来自 loader 中用defer包装的延迟数据见下文与 defer 的关系。props.children必选类型(result: T) React.ReactNode渲染函数render prop会在 Promise 解决后以解决值作为唯一参数被调用。注意它不是普通 children而是一个函数——这是它与普通Suspense子节点的语法差异。源码中的额外能力可选fallbackprop官方文档只列出上述两个必选 prop但从源码结构看Await 的组件签名 还支持一个可选的fallbackexport function AwaitT( props: AwaitOptionsT { fallback?: React.ReactNode children: (result: T) React.ReactNode }, ) { const inner AwaitInner {...props} / if (props.fallback) { return React.Suspense fallback{props.fallback}{inner}/React.Suspense } return inner }传入fallback时组件内部自动包一层React.Suspense不传则直接返回内部组件由你自己在外层提供Suspense。两种写法行为等价// 写法一由 Await 自带 fallback Await promise{deferredPromise} fallback{Spinner /} {(data) div{data}/div} /Await // 写法二外层手动包 Suspense文档示例采用此写法 Suspense fallback{Spinner /} Await promise{deferredPromise} {(data) div{data}/div} /Await /Suspense返回值与三种状态的行为定义文档对Await的返回行为给出了完整定义这三条与源码逐行对应Promise 状态Await行为源码对应pending未决挂起——向 React 抛出一个 Promise触发最近Suspense的 fallbackthrow promiseawaited.tsx#L18-L20rejected拒绝抛出错误交由错误边界捕获throw promise[TSR_DEFERRED_PROMISE].errorawaited.tsx#L22-L24resolved已解决以解决值调用children并渲染结果return promise[TSR_DEFERRED_PROMISE].data后经props.children(data)渲染awaited.tsx#L26、L52-L54AwaitInner内部把useAwaited的返回值直接传给children整个渲染路径非常短没有额外的中间状态。文档官方示例与加载延迟数据的完整链路文档给出的官方示例import { Await } from tanstack/react-router function Component() { const { deferredPromise } route.useLoaderData() return ( Await promise{deferredPromise} {(data) div{JSON.stringify(data)}/div} /Await ) }要读懂这段代码需要理解它与defer函数的关系。文档中的 defer 函数说明 指出defer把 Promise 包装成带有 deferred 状态对象的延迟 Promise随后可以传给useAwaitedHook 或Await组件。仓库中 deferred-data 示例 展示了典型的 loader 侧写法// loader 中把可能慢的 Promise 用 defer 包装后放进 loader 返回数据 commentsPromise: defer(commentsPromise)底层的defer实现在 defer.ts 中核心机制是在 Promise 上挂一个全局唯一符号属性export const TSR_DEFERRED_PROMISE Symbol.for(TSR_DEFERRED_PROMISE) export type DeferredPromiseStateT | { status: pending; data?: T; error?: unknown } | { status: success; data: T } | { status: error; data?: T; error: unknown } export function deferT(_promise: PromiseT, options?: { ... }) { const promise _promise as DeferredPromiseT // 幂等已经是 deferred promise 则原样返回 if ((promise as any)[TSR_DEFERRED_PROMISE]) { return promise } promise[TSR_DEFERRED_PROMISE] { status: pending } promise .then((data) { promise[TSR_DEFERRED_PROMISE].status success promise[TSR_DEFERRED_PROMISE].data data }) .catch((error) { promise[TSR_DEFERRED_PROMISE].status error ;(promise[TSR_DEFERRED_PROMISE] as any).error { data: (options?.serializeError ?? defaultSerializeError)(error), __isServerError: true, } }) return promise }三个细节值得注意幂等性defer对已经打标的 Promise 直接原样返回重复包装是安全的状态机pending → success | error的三态与Await的三种返回行为一一对应错误序列化被拒绝时错误会经过defaultSerializeError可用options.serializeError覆盖序列化并打上__isServerError: true标记——这为跨服务器/客户端边界传递错误保留了类型信息。与 useAwaited Hook 的关系Await本质上是对useAwaitedHook 的 render-prop 封装AwaitInner内部调用useAwaited(props)拿到数据后再调用props.children(data)。因此文档中两者的用法可以互相替换——需要条件逻辑如根据状态切换分支时用 Hook需要纯粹等待并渲染时用组件。使用要点小结适用版本Await的手动挂起机制专为 React 18 设计React 19 项目中推荐直接用use()Await仍可作为兼容封装使用。必须配对 Suspensepending状态下组件会抛出 Promise若祖先链上没有Suspense或fallbackprop应用会直接崩溃。文档示例未显示外层Suspense实际使用时务必确认祖先中存在 Suspense 边界。拒绝即错误Promise 拒绝时Await抛出的是序列化后的错误对象需要 error boundary 承接不会静默吞掉。数据来源约定传给promise的通常是 loader 中经defer包装的延迟 Promisedefer保证重复包装幂等可以放心复用。完整 API 文档Await在 API 索引中的位置见 router.md配套的defer与useAwaited说明分别在 deferFunction.md 和 useAwaitedHook.md。【免费下载链接】router A client-first, server-capable, fully type-safe router and full-stack framework for the web (React and more).项目地址: https://gitcode.com/GitHub_Trending/ro/router创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Python字符串处理与编码优化实战指南 2026/9/14 2:28:30

Python字符串处理与编码优化实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
AI论文降重技术解析:千笔AI如何实现高效查重 2026/9/14 2:28:30

AI论文降重技术解析:千笔AI如何实现高效查重

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
大语言模型技术架构与商业化应用解析 2026/9/14 2:28:30

大语言模型技术架构与商业化应用解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
数学建模竞赛数据分析:从预处理到模型构建实战 2026/9/14 2:28:30

数学建模竞赛数据分析:从预处理到模型构建实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
纯JS实现植物大战僵尸:DOM游戏开发实战指南 2026/9/14 2:28:30

纯JS实现植物大战僵尸:DOM游戏开发实战指南

简介:这是一份面向前端初学者与JavaScript进阶学习者的趣味实践项目源码,聚焦游戏逻辑实现与DOM交互开发,帮助开发者在真实场景中掌握ES6语法、面向对象编程、事件驱动机制及基础动画控制。资源包含364个文件,主体为22个核心JS脚本…

阅读更多 →
HI6421 PMIC Linux驱动移植:从手册到设备树的完整实践 2026/9/14 2:25:29

HI6421 PMIC Linux驱动移植:从手册到设备树的完整实践

简介:面向嵌入式驱动开发者的Hi6421 PMIC驱动核心源码文件,适合需要理解电源管理集成电路软件控制、I2C/SPI通信及低功耗策略的研发场景。资源以RAR压缩包形式提供,压缩包内仅含1个C源文件,整体大小约1KB,属轻量级驱动…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞