React Relay requestSubscription 深度指南:命令式建立 GraphQL 订阅的完整实战
React Relay requestSubscription 深度指南命令式建立 GraphQL 订阅的完整实战【免费下载链接】relayRelay is a JavaScript framework for building>项目地址: https://gitcode.com/gh_mirrors/relay29/relayrequestSubscription是 React Relay 提供的命令式ImperativeAPI用于在 JavaScript 代码中直接建立一条 GraphQL Subscription从而在服务端事件流到达时实时获取并更新数据。本指南以requestSubscription的官方 API 参考文档为主体结合relay-runtime中该 API 的实际源码实现与测试用例系统讲解它的参数、配置、返回类型、底层执行链路以及与 Hook 版useSubscription的关系帮助你掌握在非组件场景如工具函数、事件处理器、数据层逻辑中安全使用订阅的完整方案。requestSubscription是什么requestSubscription定义在react-relay包中是一个命令式 API调用它时传入一个 Relay Environment 与一份订阅配置Relay 会立即通过该 Environment 建立订阅并返回一个可用于取消订阅的Disposable对象。import {graphql, requestSubscription} from react-relay; const subscription graphql subscription UserDataSubscription($input: InputData!) { # ... } ; function createSubscription(environment: IEnvironment): Disposable { return requestSubscription(environment, { subscription, variables: {input: {userId: 4}}, }); }与声明式的useSubscriptionHook 不同requestSubscription不依赖 React 组件的生命周期你可以把它放在任意普通函数、事件回调或非 React 模块中只要手里有一个 Environment 实例即可建立订阅并由调用方自行管理订阅的销毁时机。关于订阅的整体概念包括订阅根字段subscription root field、服务端事件流与查询分离的两段式处理模型可参阅 GraphQL subscriptions 引导教程关于如何在 React 组件中以声明式方式订阅可参阅 useSubscription API 与 Updating Data 引导章节。参数ArgumentsrequestSubscription接受两个参数参数类型说明environmentIEnvironment一个 Relay Environment 实例订阅将建立在该 Environment 之上configGraphQLSubscriptionConfig描述订阅如何建立、如何处理数据与错误的配置对象config中最核心的三个字段是subscription一个GraphQLTaggedNode即用graphql模板字面量声明的 Subscription 操作variables传给订阅的变量对象一系列可选回调与更新器onCompleted、onError、onNext、updater用于处理订阅生命周期内的各类事件。在 GraphQLSubscriptionConfig 类型参考 中完整的字段定义如下。GraphQLSubscriptionConfigTSubscriptionPayload完整配置详解GraphQLSubscriptionConfig是一个泛型配置对象其字段含义如下字段必选类型说明subscription是GraphQLTaggedNode使用graphql模板字面量声明的 GraphQL Subscriptionvariables是Variables传给订阅的变量cacheConfig可选CacheConfig订阅的缓存与执行相关配置onCompleted可选() void订阅成功建立时执行的回调onError可选(Error) {}发生错误时执行的回调onNext可选(TSubscriptionPayload) {}收到新数据时执行的回调updater可选SelectorStoreUpdater收到订阅负载后如何更新 Relay Store 的更新器函数结合源码relay-runtime中的类型定义见 requestSubscription.js 第 44–54 行该类型还额外包含一个可选的configs字段用于传入声明式变更配置Declarative Mutation Configexport type GraphQLSubscriptionConfigTVariables, TData, TRawResponse Readonly{ configs?: ArrayDeclarativeMutationConfig, cacheConfig?: CacheConfig, subscription: GraphQLSubscriptionTVariables, TData, TRawResponse, variables: NoInferTVariables, onCompleted?: ?() void, onError?: ?(error: Error) void, onNext?: ?(response: ?TData) void, updater?: ?SelectorStoreUpdaterTData, };注意updater与configs二者只能二选一。源码中在两者同时传入时会抛出warning提示Expected only one ofupdaterandconfigsto be provided若提供了configsRelay 会通过RelayDeclarativeMutationConfig.convert(...)将其转换为实际的updater见 requestSubscription.js。Flow 类型参数TSubscriptionPayloadGraphQLSubscriptionConfig是泛型类型参数TSubscriptionPayload表示订阅向客户端提供的 payload 类型。你应该使用编译器自动生成的.graphql文件导出的类型作为该类型参数例如import type {UserDataSubscription} from ./__generated__/UserDataSubscription.graphql;之后可将UserDataSubscription作为类型参数传入让onNext回调的参数获得完整的类型检查支持。cacheConfig订阅的缓存与执行控制cacheConfig完整定义见 CacheConfig 类型参考是一个可选对象用于控制订阅在环境层面的执行行为字段类型说明forceboolean可选为true时无条件发起查询忽略任何已配置的响应缓存的当前状态pollnumber可选以毫秒为单位的轮询间隔使查询按该间隔实时更新该值会被传给setTimeoutliveConfigIdstring可选通过调用 GraphQLLiveQuery 实现查询实时更新表示执行 live query 时网关使用的配置metadataobject可选用户提供的元数据transactionIdstring可选用户提供的值用于作为某次操作执行的唯一标识尽管force、poll等字段更多用于查询场景订阅同样会把它传给环境执行层——在requestSubscription的源码中cacheConfig会被直接传给createOperationDescriptor(subscription, variables, cacheConfig)成为操作描述符Operation Descriptor的一部分见 requestSubscription.js。updater与SelectorStoreUpdater如何更新 Relay Store默认情况下Relay 会根据订阅的字段选择自动规范化normalize负载并合并进 Store。若需要更精细的控制——例如根据负载创建新记录、更新或删除已有记录——可以提供updater回调。SelectorStoreUpdater是一个签名如下的函数详见 SelectorStoreUpdater 类型参考(store: RecordSourceSelectorProxy, data) void通过该接口你可以命令式地直接读写 Relay Store既能创建全新的记录也能更新或删除已有的记录从而完全掌控 Store 对订阅负载的响应方式。读写 Store 的完整 API 参见 Store 的RecordSourceSelectorProxy参考。返回值DisposablerequestSubscription返回一个Disposable对象接口定义见 Disposable 类型参考其结构如下interface Disposable { dispose: () void; }调用dispose()即可清除取消该订阅停止接收后续数据。在组件卸载、页面离开或业务逻辑结束时及时调用dispose是避免订阅泄漏的关键。在源码中返回的Disposable实际上是对环境执行结果订阅的unsubscribe的包装const sub environment .executeSubscription({ operation, updater, }) .subscribe({ complete: onCompleted, error: onError, next: responses { /* ... */ }, }); return { dispose: sub.unsubscribe, };见 requestSubscription.js。源码级原理剖析requestSubscription的执行链路阅读relay-runtime中 requestSubscription.js 的实现可以还原这条完整的执行链路校验操作类型通过getRequest(config.subscription)获取请求定义若params.operationKind ! subscription立即抛出Error(requestSubscription: Must use Subscription operation)。也就是说向该 API 传入 query 或 mutation 会在建立订阅前就被拒绝。创建操作描述符调用createOperationDescriptor(subscription, variables, cacheConfig)将订阅、变量与缓存配置打包成环境可执行的操作描述符。处理更新器若提供了configs声明式配置通过RelayDeclarativeMutationConfig.convert(...)转换为updater否则直接使用配置中传入的updater。执行订阅调用environment.executeSubscription({operation, updater})得到一个可观察对象Observable并订阅它的complete、error、next三路事件分别对应onCompleted、onError、onNext回调。读取并派发新数据在next回调中Relay 会从响应中解析extensions.__relay_subscription_root_id可能是顶层或数组首项中的扩展字段若存在则据此通过createReaderSelector重新构造选择器再执行environment.lookup(selector)从 Store 中读取最新的规范化数据最后把data作为参数调用onNext。这意味着onNext收到的 payload 是经过 Relay Store 规范化、可被组件直接消费的数据。返回取消句柄最终返回{dispose: sub.unsubscribe}。类型参数与泛型约束在 requestSubscription.d.ts 中API 被泛型化声明为requestSubscriptionTVariables extends Variables, TData, TRawResponse(environment, config): Disposable其中TVariables受Variables约束。配合编译器生成的类型onNext的 payload、variables的字段都具备类型安全。与useSubscriptionHook 的关系对于 React 组件内的订阅场景官方推荐使用 Hook 版useSubscription。查看 useSubscription.js 的实现可以发现它本质上只是requestSubscription的 React 封装通过useRelayEnvironment()获取当前环境在useEffect中调用requestSubscription(environment, config)在 effect 的清理函数中返回dispose即组件卸载时自动取消订阅依赖数组为[environment, config, requestSubscriptionFn]意味着config对象需要被 memoize否则每次渲染都会重新订阅源码注释中明确提示请勿内联定义配置对象。两者适用范围可归纳为场景推荐 APIReact 组件内部订阅生命周期跟随组件useSubscription普通函数、事件处理器、工具模块、非 React 代码requestSubscription需要在多环境 / 手动控制销毁时机的场景requestSubscription行为与注意事项根据 API 参考文档Behavior 一节并结合源码使用requestSubscription时需要注意命令式建立订阅调用即建立不依赖组件渲染订阅由返回的Disposable负责清理。只接受 Subscription 操作传入其他操作类型会立即抛错Must use Subscription operation这是源码层面强制的约束。onCompleted语义按文档定义onCompleted是订阅建立成功时执行的回调它被绑定到可观察对象的complete事件上具体触发时机取决于 Environment 网络层的实现。onNext的 payload 类型payload 是订阅负载经过 Relay Store 规范化后的数据。若响应扩展中存在__relay_subscription_root_idRelay 会用它作为新的 root id 重新 lookup确保多个订阅复用同一根记录时数据读取正确。更新器的二选一约束updater与configs不能同时提供同时提供时源码会给出 warning并优先走configs的声明式转换路径。事件流与字段选择无关如 GraphQL subscriptions 引导教程 所述订阅事件流可以与所选字段完全无关——即服务端事件发生并不保证所选值一定变化客户端仍会收到通知。记得清理在页面卸载、组件销毁或业务结束时调用dispose()避免订阅句柄泄漏导致内存占用与不必要的网络连接。测试验证仓库中的测试 requestSubscription-test.js 覆盖了该 API 的核心行为包括使用requestSubscription(environment, {...})建立订阅并断言其回调行为专门的describe(requestSubscription() cacheConfig, ...)测试块验证cacheConfig的传递路径对updater/configs组合、onNext数据读取等分支的覆盖。这些测试与上述源码分析相互印证可作为理解requestSubscription契约行为的补充参考。实际项目中若想验证自定义订阅逻辑也可以参考测试中对MockEnvironment与 payload 注入的使用方式在单元测试中模拟服务端推送。相关参考requestSubscription API 参考文档GraphQLSubscriptionConfig 类型参考CacheConfig 类型参考SelectorStoreUpdater 类型参考Disposable 类型参考GraphQL subscriptions 引导教程实现源码requestSubscription.js、useSubscription.js测试用例requestSubscription-test.js【免费下载链接】relayRelay is a JavaScript framework for building>项目地址: https://gitcode.com/gh_mirrors/relay29/relay创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考