ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Relay 19 的 `fetchQuery` 完整指南:React 之外的命令式查询获取、去重与数据保留

Relay 19 的 `fetchQuery` 完整指南:React 之外的命令式查询获取、去重与数据保留 Relay 19 的fetchQuery完整指南React 之外的命令式查询获取、去重与数据保留【免费下载链接】relayRelay is a JavaScript framework for building>项目地址: https://gitcode.com/gh_mirrors/relay29/relayfetchQuery是 Relay 官方提供的命令式imperative查询 API用于在 React 组件渲染之外主动发起 GraphQL 查询并读取结果。它返回一个基于RelayObservable的可观察对象支持subscribe/toPromise两种消费方式并内置同请求去重、自动写入 Relay Store 等能力。读完本文你将掌握fetchQuery的参数与返回对象细节、正确的订阅与取消模式、store-or-network与network-only两种 fetch policy 的差异、数据保留GC陷阱以及它与toPromise、loadQuery、旧版fetchQuery_DEPRECATED之间的取舍并可从源码与测试用例层面理解其实现原理。为什么需要fetchQuery在 Relay 的应用里大多数数据获取发生在 React 组件内部通过useLazyLoadQuery、useFragment等 Hooks 完成此时 Relay 会自动管理请求的生命周期。但有一类场景发生在“React 之外”在事件处理器例如按钮点击、表单提交中按需拉取数据在路由守卫、服务端脚本、非 React 模块中执行一次性查询需要手动控制请求的订阅、取消与错误处理而不是依赖 Suspense。fetchQuery正是为这些场景设计的它在react-relay中导出同时也在relay-runtime中实现接受 environment、GraphQL 查询与变量返回一个可观察对象只有当你真正订阅subscribe或转换为 PromisetoPromise时网络请求才会发出。从仓库的导出入口看react-relay/index.js 从RelayRuntime解构出fetchQuery并暴露为公开 API见 react-relay/index.js同时一并导出了旧版fetchQuery_DEPRECATEDreact-relay/index.js方便不同代际代码共存迁移。基本用法在 React 之外发起查询官方文档给出的最小示例是通过.subscribe()订阅一个查询// 优先使用 useRelayEnvironment() 返回的 environment const MyEnvironment require(MyEnvironment); const {fetchQuery} require(react-relay); fetchQuery( environment, graphql query AppQuery($id: ID!) { user(id: $id) { name } } , {id: 4}, ).subscribe({ start: () {...}, complete: () {...}, error: (error) {...}, next: (data) {...}, });关键点environment承载查询执行的 Relay Environment 实例。如果你在 React 组件内发起请求应该使用useRelayEnvironment()拿到的环境以保证与组件共享同一个 Store 和网络层query用graphql模板字面量书写的查询。编译期会把它转换为GraphQLTaggedNode运行时通过getRequest()解析见 fetchQuery.jsvariables与查询内声明变量匹配的对象如{id: 4}options可选见下文参数表。参数一览参数类型说明environmentIEnvironment执行请求的 Relay Environment 实例queryGraphQLTaggedNode用graphql模板字面量定义的查询variablesVariables与查询声明变量匹配的变量对象options.fetchPolicynetwork-only \| store-or-network获取策略默认network-onlyoptions.networkCacheConfig.forceboolean是否绕过网络响应缓存默认为true从 relay-runtime/query/fetchQuery.js 的函数签名可以看到fetchQuery的options实际支持两个字段fetchPolicy与networkCacheConfig后者即CacheConfig类型。实现中networkCacheConfig默认被展开为{force: true, ...}fetchQuery.js也就是说默认每次调用都会绕过网络响应缓存、直接打网络只有当你显式传入force: false时才可能命中RelayQueryResponseCache之类的网络层缓存。CacheConfig在 RelayRuntimeTypes.js 中定义还包含poll、liveConfigId、metadata、transactionId等字段可用于轮询、实时订阅透传等高级用途。注虽然 API 参考文档只列了networkCacheConfig.force一个选项但当前仓库版本Relay 19 代码线的fetchQuery实现已额外支持fetchPolicy。FetchQueryFetchPolicy类型只包含store-or-network | network-only两个合法值传入其他值会抛出fetchQuery: Invalid fetchPolicy错误fetchQuery.js。返回的可观察对象subscribe 与 toPromisefetchQuery返回一个RelayObservable。请求不会在调用时自动发出——只有调用.subscribe()或.toPromise()才会真正启动网络请求。.subscribe(observer)subscribe接受一个 observer 对象可为以下事件注册回调回调触发时机收到的参数start网络请求开始时subscription网络可观察对象上的订阅next每次从网络收到 payload 时data收到 payload 时从 Relay Store 读取的查询数据快照complete网络请求成功完成时无error网络请求出错时error发生的错误unsubscribe订阅被取消时subscription网络可观察对象上的订阅subscribe的返回值是一个subscription对象调用subscription.unsubscribe()可以取消正在进行的网络请求。这一行为在源码注释中有明确描述“如果 subscribe 返回的 disposable 在请求 in-flight 期间被调用请求将被取消”fetchQuery.js。需要注意subscribe只是订阅“查询的获取过程”并不会订阅 Relay Store 中数据的后续变化。如果你需要持续跟踪数据变更应该改用useSubscribeToInvalidationState、observeFragmentExperimental或组件内的 Hooks 方案。.toPromise()toPromise()把可观察对象转换为 Promiseconst {fetchQuery} require(react-relay); fetchQuery( environment, graphql query AppQuery($id: ID!) { user(id: $id) { name } } , {id: 4}, ) .toPromise() // 注意不建议使用可能导致数据缺失 .then(data {...}) .catch(error {...});官方文档对toPromise给出了明确警告Promise 在第一个网络响应到达时就会 resolve并取消后续处理。这意味着查询中带有的 deferreddefer、3Dmodule等增量数据可能不会被处理结果对象中会缺失这些部分。因此官方明确表示一般不建议使用toPromise()。此外toPromise返回的 Promise 无法被取消。两种 fetchPolicy 的行为差异fetchPolicy决定fetchQuery如何满足数据请求network-only默认无条件向网络发起请求然后从 Store 读取最新快照作为next的数据。这是文档所述“默认每次绕过网络响应缓存”的行为在 Store 层面的对应。store-or-network先调用environment.check(operation)检查 Store 中查询数据是否完整可用如果状态为available直接environment.lookup()返回本地数据不再发网络请求否则退回网络获取fetchQuery.js。实现中无论哪种策略最终都经由fetchQueryInternal.fetchQuery()执行网络层请求并把每次响应写回 Store 后再通过environment.lookup(operation.fragment)读出数据快照fetchQuery.js。同时实现会通过environment.__log({name: fetchquery.fetch, ...})记录一次内部日志事件方便你接入 Relay DevTools 或自定义日志观察该次获取的决策过程fetchQuery.js。三大行为特性自动写库、不保留数据、自动去重1. 自动写入 StorefetchQuery会把服务端返回的数据自动规范化并写入内存中的 Relay Store同时通知所有订阅了相关数据的组件重新渲染。也就是说即使组件没有直接发起这次查询只要它订阅了查询涉及的 fragment 数据就会收到更新。2. 不保留数据GC 陷阱fetchQuery不会对查询数据执行retain()因此请求完成后数据随时可能被 Relay 的垃圾回收机制从 Store 中清除——它不保证数据在请求作用域之外仍然存在。官方文档建议如果你需要在请求之外保住这些数据请直接调用environment.retain()持有查询引用。详见文档 Controlling Relays GC Policy仓库中对应指南位于 website/versioned_docs/version-v17.0.0/guided-tour/reusing-cached-data。这一行为同样被测试用例直接验证在 relay-runtime/query/tests/fetchQuery-test.js 中测试fetches request and does not retain datamock 了environment.retain在complete回调里断言retained.length 0证实fetchQuery全程不会触发 retain。3. 同请求自动去重fetchQuery会对“相同 environment、相同查询、相同变量”且同时 in-flight的请求自动去重后发起的调用会复用第一个请求的响应而不是重复打网络。去重缓存的核心实现位于 relay-runtime/query/fetchQueryInternal.js每个 environment 对应一个MapRequestIdentifier, RequestCacheEntryRequestIdentifier由查询与变量唯一确定见 getRequestIdentifier请求首次启动时创建RelayReplaySubject缓存所有网络响应事件后续同标识订阅者直接从这个 subject 拿到回放请求完成成功或失败后缓存条目被finally清理若某个 in-flight 请求的所有订阅者都取消订阅底层网络订阅也会被取消并从缓存中移除fetchQueryInternal.js。一个需要注意的边界如果请求是同步完成的例如命中同步缓存在同一个 tick 内再次调用相同参数的fetchQuery不会去重因为此时请求已经不在 in-flight 状态fetchQueryInternal.js。在组件内使用配合useRelayEnvironment()虽然fetchQuery面向 React 之外的场景但官方文档特别指出如果在 React 组件内发起请求应使用useRelayEnvironment()获取 environment。示例import {useCallback} from react; import {useRelayEnvironment, fetchQuery, graphql} from react-relay; function useRefreshUser(id) { const environment useRelayEnvironment(); return useCallback(() { fetchQuery(environment, UserQuery, {id}).subscribe({ complete: () console.log(user refreshed), error: (error) console.error(error), }); }, [environment, id]); }这样做能保证该请求与当前 React 树共享同一个 Store 与网络层写回的数据可以被组件内相关 fragment 立即观察到。与其他 API 的关系与取舍useLazyLoadQuery/loadQuery面向组件内/EntryPoint 的数据预加载由 React 或 Router 管理生命周期与数据保留是“响应式获取”的首选fetchQuery面向一次性、命令式获取数据不保留、需手动订阅或转 Promise适合事件驱动场景fetchQuery_DEPRECATED旧版 API直接返回 Promise内部等价于environment.execute(...).map(lookup).toPromise()见 relay-runtime/query/fetchQuery_DEPRECATED.js。它天然具备toPromise的所有缺点首个响应即 resolve、无法取消在新代码中应优先使用返回 observable 的新版fetchQueryenvironment.execute更底层的网络执行入口fetchQuery在去重与快照读取之上对它的封装。总结fetchQuery是 Relay 在 React 之外执行查询的官方入口调用后返回RelayObservable订阅才开始发请求数据自动写入 Store但不会 retain请求结束后可能被 GC相同参数的 in-flight 请求会被自动去重。日常使用中建议组件内使用useRelayEnvironment()获取 environment优先用.subscribe()而非.toPromise()避免 deferred / 3D 数据丢失若需在请求结束后长期保留数据显式调用environment.retain()需要命中 Store 时使用store-or-network需要强制走网络默认行为时保持network-only。【免费下载链接】relayRelay is a JavaScript framework for building>项目地址: https://gitcode.com/gh_mirrors/relay29/relay创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表