
前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载本篇技术指南围绕 Relay 中“以不同数据重取 FragmentRefetching Fragments with Different Data”这一核心场景展开讲解如何使用useRefetchableFragmentHook 与refetchable指令让 Fragment 在保留查询上下文的前提下用一组全新的变量重新取数并渲染从而支持“切换当前选中项”“渲染不同的列表内容”等交互需求。读完本文你将掌握refetchable的适用条件、refetch函数的变量与选项语义、以及需要避开 Suspense 时的fetchQuery替代方案并能理解 Relay 编译器如何自动生成 refetch 查询的底层机制。理解“以不同数据重取 Fragment”在 Relay 中所谓refetch a fragment重取 Fragment指的是拉取一份与该 Fragment 最初渲染时不同的数据。典型场景包括改变当前选中的条目如切换商品、切换评论渲染与当前显示不同的列表内容更一般地把当前已渲染的内容过渡到新的或不同的内容。概念上这意味着把当前已渲染的 Fragment重新放到一个新的查询根query root下用不同的变量再取一次、再渲染一次。这里有一个关键前提Fragment 无法独立取数它必须依附于某个查询query。因此我们无法单独“fetch 一个 fragment”而必须借助自动生成的查询来承载重取。这正是useRefetchableFragment存在的意义配合refetchable指令Relay 编译器会自动生成一个用于重取该 Fragment 的查询Hook 返回的refetch函数则负责以新变量执行这个查询并让组件用最新数据重新渲染。使用useRefetchableFragment重取 Fragment基础示例切换评论正文的语言先看一个完整可运行的例子一个展示评论正文的CommentBody组件点击按钮即可把正文翻译成西班牙语重新取数。import type {CommentBodyRefetchQuery} from CommentBodyRefetchQuery.graphql; import type {CommentBody_comment$key} from CommentBody_comment.graphql; type Props { comment: CommentBody_comment$key, }; function CommentBody(props: Props) { const [data, refetch] useRefetchableFragmentCommentBodyRefetchQuery, _( graphql fragment CommentBody_comment on Comment # refetchable 让 Relay 为该 fragment 自动生成查询 refetchable(queryName: CommentBodyRefetchQuery) { body(lang: $lang) { text } } , props.comment, ); const refetchTranslation () { // 传入新变量调用 refetch // 它会以新变量重取 refetchable 查询 // 并用最新取回的数据更新当前组件。 refetch({lang: SPANISH}); }; return ( p{data.body?.text}/p Button onClick{() refetchTranslation()} Translate Comment /Button / ); }逐步拆解这个示例useRefetchableFragment的用法与useFragment类似参见 Fragments 一节但有几点差异它要求传入的 Fragment必须标注refetchable指令。注意refetchable只能加在“可重取”的 Fragment 上即声明在Viewer、Query类型上或声明在任何实现了Node接口即拥有id字段的类型上以及fetchable类型上。它返回一个refetch函数该函数已被 Flow 类型化其参数类型正好是生成的查询所期望的变量集合。它接受两个 Flow 类型参数第一个是自动生成查询的类型本例为CommentBodyRefetchQuery第二个通常可以被自动推断只需传下划线_。调用refetch需要两类输入第一个参数是新变量集合。传入一组新变量会让 Fragment 以这组新变量重新取数。你只需提供refetchable查询所需变量的一个子集如果 Fragment 所在类型有id字段查询会要求一个id其余变量则是 Fragment 内被传递引用的那些变量。本例中我们传入了当前评论的id和新的lang变量值以取回翻译后的正文。第二个是可选的 options 参数本例未传因此使用默认的fetchPolicystore-or-network——如果该 Fragment 的新数据已缓存则跳过网络请求参见 Reusing Cached Data For Render 相关章节。调用refetch会触发组件重新渲染并可能使useRefetchableFragment进入 Suspense参见 Loading States with Suspense。因此必须保证该组件上方有一个Suspense边界以便在取数期间展示 fallback 加载态。:::info 同样的行为也适用于usePaginationFragment返回的refetch函数参见 usePaginationFragment API。 :::refetch的变量与选项语义结合 useRefetchableFragment API 参考refetch的完整签名与语义如下variables对象用于重取refetchable查询的新变量值需与 Fragment 内引用的 GraphQL 变量一致。但有两个重要的放宽规则只需要提供打算改变的变量Fragment 引用但被省略的变量会回退到原始父查询中的值。因此若想以与最初完全相同的变量重取 Fragment直接调用refetch({})即可。对于$id变量除非你想用不同的id重取否则传id是可选的——Relay 已经知道当前渲染对象的 id。其refetch返回一个disposable对象调用disposable.dispose()可取消本次重取请求。options可选对象fetchPolicy决定是否使用缓存数据、以及在缓存可用时何时发起网络请求完整规范见 Fetch Policies。onComplete每当重取请求完成时包括任何增量数据载荷都会被调用的回调。useRefetchableFragment还具备与useFragment相同的订阅语义组件自动订阅 Fragment 数据更新如果该对象的数据在应用其他位置被更新如新取数或 mutation组件会自动用最新数据重新渲染当 Fragment 的某些数据缺失且正在被父查询获取时组件会挂起suspend。与旧版RefetchContainer的差异从useRefetchableFragment的实现历史与 API 设计看它与 class 时代的RefetchContainer相比主要有三点变化无需再手写 refetch 查询Relay 通过refetchableFragment 自动生成不再区分语义模糊的refetchVariables与renderVariables重取总是以你提供的变量正确地取数并渲染省略的变量回退到父查询原值重取总是明确地更新组件而RefetchContainer时期取决于 refetch 查询内容与 Fragment 定义的对象类型更新不一定发生。需要避开 Suspense 时fetchQuery 手动加载态有些场景下你不希望出现会隐藏已渲染内容的 Suspense fallback。此时可以用fetchQuery先把数据写入本地 Store再手动维护一个加载状态import type {CommentBodyRefetchQuery} from CommentBodyRefetchQuery.graphql; import type {CommentBody_comment$key} from CommentBody_comment.graphql; type Props { comment: CommentBody_comment$key, }; function CommentBody(props: Props) { const [data, refetch] useRefetchableFragmentCommentBodyRefetchQuery, _( graphql fragment CommentBody_comment on Comment # refetchable 让 Relay 为该 fragment 自动生成查询 refetchable(queryName: CommentBodyRefetchQuery) { body(lang: $lang) { text } } , props.comment, ); const [isRefetching, setIsRefreshing] useState(false) const refetchTranslation () { if (isRefetching) { return; } setIsRefreshing(true); // fetchQuery 会先取回查询并把数据写入 Relay store // 从而保证当我们重新渲染时数据已在缓存中、不会挂起。 fetchQuery(environment, AppQuery, variables) .subscribe({ complete: () { setIsRefreshing(false); // 查询取回 *之后*再次调用 refetch 用最新数据渲染。 // 此时该查询的数据已缓存因此用 store-only 的 // fetchPolicy 来避免挂起。 refetch({lang: SPANISH}, {fetchPolicy: store-only}); } error: () { setIsRefreshing(false); } }); }; return ( p{data.body?.text}/p Button disabled{isRefetching} onClick{() refetchTranslation()} Translate Comment {isRefetching ? LoadingSpinner / : null} /Button / ); }这个方案的关键点重取时我们自己维护isRefetching状态因为避开了挂起用它渲染忙碌指示器或类似加载 UI而不会隐藏已有内容事件处理器中先调用fetchQuery它会取回查询并把数据写入本地 Relay Store网络请求完成后再调用refetch用更新后的数据渲染与前面的示例类似此时调用refetch时该 Fragment 的数据应当已经缓存在本地 Store 中因此使用fetchPolicy为store-only只读取已缓存数据避免挂起。需要说明的是这一“避开 Suspense”的做法是当前版本的权宜方案。原文档的附注OssAvoidSuspenseNote指出在未来支持并发渲染的 React 版本中React 将提供相应选项避免挂起时用 Suspense fallback 隐藏已渲染内容。深入底层refetch是如何工作的为了把前面的实操与 Relay 的实现对上号这里结合本仓库源码梳理refetch的调用链以下文件均在 packages/react-relay 下useRefetchableFragment.js 是公开入口。它通过getFragment拿到 fragment node然后委托给内部实现useRefetchableFragmentInternal返回[fragmentData, refetch]元组。useRefetchableFragmentInternal.js 是核心实现几个值得注意的细节印证了文档语义调用refetch时源码把parentVariables、fragmentVariables与你传入的providedRefetchVariables合并得到最终的重取变量集合——这正是“省略的变量回退到父查询原值”的实现依据当 Fragment 有标识字段id等而你未显式传入时源码会从当前 Fragment 数据中读取identifierValue自动补上这就是“传id是可选的”的实现依据重取请求通过createOperationDescriptor创建强制force: true再经loadQuery启动网络请求必要时随后在渲染阶段通过QueryResource.prepare读取/等待结果并在重取查询仍在途时挂起——对应“调用 refetch 可能让组件 suspend”的行为refetch返回{dispose: disposeQuery}即文档中的 disposable可用于取消本次重取。在__DEV__模式下实现还带有一组校验函数checkSameIDAfterRefetch、checkSameTypeAfterRefetch会在重取后检查返回对象的id与__typename是否与 Store 中一致若不一致会给出 warning——这保证了以不同数据重取时数据一致性问题的可诊断性。编译器侧refetchable如何生成查询refetchable的编译端实现在 compiler/crates/relay-transforms/src/refetchable_fragment/ 目录。从 refetchable_directive.rs 的定义可以看到该指令的完整 schema 为directive refetchable( queryName: String! directives: [String!] preferFetchable: Boolean ) on FRAGMENT_DEFINITION其中queryName用于指定自动生成查询的名字即文档示例中的CommentBodyRefetchQuery。同目录下的多个生成器对应不同 Fragment 宿主类型印证了“可重取”条件的三种情形viewer_query_generator.rsFragment 声明在Viewer类型上时生成查询query_query_generator.rsFragment 声明在Query类型上时生成查询node_query_generator.rsFragment 声明在实现了Node接口有id字段的类型上时生成查询fetchable_query_generator.rs对应fetchable类型的场景。生成的查询类型会自动导出到queryName.graphql.js文件供你像示例中那样import type {CommentBodyRefetchQuery}使用。小结以不同数据重取 Fragment 是 Relay 构建动态、可交互 UI 的基础能力。核心要点可归纳为理解语义重取 把 Fragment 放到新的查询根下、用新变量重新取数渲染Fragment 不能独立取数必须依托自动生成的查询。使用useRefetchableFragment在 Fragment 上标注refetchable(queryName: ...)用refetch(variables, options)触发重取变量只传需要改变的id可省略fetchPolicy默认store-or-network。管理加载态默认路径下确保组件上方有Suspense边界若要保留已渲染内容可用fetchQuery预取 store-only策略手动维护加载状态。理解原理编译器侧的refetchable_fragmenttransform 负责生成查询运行时侧的useRefetchableFragmentInternal负责变量合并、id自动补齐与请求编排前后端共同支撑这一能力。赞分享前端开发工具【免费下载链接】relayRelay is a JavaScript framework for building>项目地址https://gitcode.com/gh_mirrors/relay29/relay点击查看免费下载相关推荐goscan性能优化如何快速扫描大型企业网络环境goscan性能优化如何快速扫描大型企业网络环境 goscan是一款简单高效的IPv4网络扫描工具能够快速发现局域网内所有活跃设备。对于大型企业网络环境网前端开发工具Relay useFragment Hook 完全指南从 Fragment Reference 到数据订阅与 SuspenseRelay useFragment Hook 完全指南从 Fragment Reference 到数据订阅与 Suspense useFragment 是 R前端开发工具BlueBubbles Server核心功能解析从私有API到WebSocket实时通信BlueBubbles Server核心功能解析从私有API到WebSocket实时通信 BlueBubbles Server是BlueBubbles应用生态前端开发工具上一篇DiffusionDet训练完全指南从数据准备到模型优化下一篇如何快速使用霞鹜臻楷面向新手的完整字体安装指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考