
refine 实时数据机制详解:usePublish 钩子与 liveProvider.publish 事件发布【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refineusePublish是 refine v3 实时(Realtime)能力中用于主动发布事件的核心 Hook,它直接返回liveProvider中的publish方法,让开发者可以在客户端向任意 channel 发布自定义事件。本篇指南基于 refine 3.x 版本文档与pankod/refine-core源码,完整讲解usePublish的使用方法、LiveEvent事件结构的四个必填属性、Hook 在 Context 中的实现原理,以及 refine 各 mutation 钩子如何自动调用它发布created/updated/deleted事件,帮助你在内部工具与后台管理面板中构建可靠的实时数据同步方案。一、usePublish 的定位:liveProvider 的 publish 方法出口refine 通过Refine组件的liveProviderprop 接入实时能力,支持 Ably、Socket.IO、Mercure、Supabase、Hasura、GraphQL Subscriptions 等任意实时方案。一个 live provider 的标准结构如下(摘自官方 Live Provider 文档):const liveProvider { subscribe: ({ channel, params: { ids }, types, callback }) any, unsubscribe: (subscription) void, publish?: (event) void, };其中subscribe与unsubscribe是必填方法,分别由useSubscription等 Hook 内部消费;而可选的publish方法则由usePublish对外暴露:usePublishreturns thepublishmethod fromliveProvider. It is useful when you want to publish a custom event. —— 来源:usePublish 官方文档也就是说,refine 本身不关心你底层用哪家实时服务,usePublish只是一个桥:把你在Refine中注入的 provider 的publish方法,原样交给业务代码调用。refine 还在内部的 mutation 钩子中使用该 Hook,在 mutation 成功后自动发布事件(详见第五节)。二、基础用法:发布一个自定义事件以下用法完整继承自官方文档:import { usePublish } from pankod/refine-core; const publish usePublish(); publish({ channel: custom-channel-name, type: custom-event-name, payload: { ids: [1, 2, 3], custom-property: custom-property-value, }, date: new Date(), });调用链非常直接:usePublish()返回的函数就是liveProvider.publish本身,参数会原封不动地透传给 provider 的publish方法,由你在 provider 中决定如何把事件投递到具体的实时服务(例如 Ably 的channel.publish(event.type, event))。::: 注意(官方 caution)该方法用于在客户端发布事件。官方明确指出:在客户端发布事件并不被推荐,最佳实践是从服务端发布事件。哪些事件必须从服务端发布,可参考 Live Provider 文档中的 Publish Events from API 一节。:::因此usePublish的典型适用场景是:发布业务自定义事件(如通知其他页面刷新、触发某个自定义 channel 的逻辑),而不是替代服务端对数据变更的广播——数据变更事件(created/updated/deleted)应优先由 API 侧发布。三、Publish 属性详解:LiveEvent 事件结构publish接收的参数会被原样传递给liveProvider的publish方法。官方文档要求关注以下四个属性,并提示类型定义参见LiveEvent接口:3.1 四个必填属性属性是否必填说明channel必填要发布事件的 channel 名称type必填要发布的事件名称payload必填事件携带的载荷数据date必填事件发生的时间这些约束在源码的类型定义中可以得到印证。LiveEvent定义于 packages/core/src/contexts/live/types.ts:export type LiveEvent { channel: string; type: deleted | updated | created | * | string; payload: { ids?: BaseKey[]; [x: string]: any; }; date: Date; meta?: MetaQuery { dataProviderName?: string; }; };从源码结构看,各字段有以下细节:channel:string类型。refine 内置的数据钩子约定资源 channel 的命名格式为resources/${resource}(例如resources/posts),自定义事件则可使用任意名称,如custom-channel-name;type:联合类型deleted | updated | created | * | string,前三种是 refine 实时体系的保留事件类型,*用于订阅侧表示通配,业务自定义事件名也允许任意字符串;payload:必须提供一个对象,其中可选地携带ids数组(BaseKey[])。ids是实时过滤的关键——订阅侧的useOne/useMany会依据params.ids与事件payload.ids的交集来决定是否触发回调,因此发布事件时务必带上受影响的记录 id;除此之外payload支持任意自定义字段(如示例中的custom-property);date:标准Date对象,表示事件发生时刻;meta(源码中存在,官方属性表中未单独列出):可选字段,可携带dataProviderName等元信息,属于 refine 内部的扩展位,一般业务代码无需手动设置。3.2 事件结构的两个典型形态refine 内置 mutation 事件(由数据钩子自动发布,channel 固定为resources/${resource}):{ channel: resources/posts, type: created, payload: { ids: [id-of-created-post] }, date: new Date(), }自定义事件(由usePublish手动发布,channel/type/payload 完全自由):{ channel: custom-channel-name, type: custom-event-name, payload: { ids: [1, 2, 3], custom-property: custom-property-value, }, date: new Date(), }四、实现原理:usePublish 如何拿到 publish 方法usePublish的完整实现非常短,位于 packages/core/src/hooks/live/usePublish/index.ts:import { useContext } from react; import { LiveContext } from contexts/live; import type { LiveProvider } from ../../../contexts/live/types; export const usePublish: () NonNullableLiveProvider[publish] () { const { liveProvider } useContext(LiveContext); return liveProvider?.publish; };从源码结构看,其工作方式为:取值来源:通过 React 的useContext(LiveContext)从上下文取出liveProvider。LiveContext由 packages/core/src/contexts/live/index.tsx 中的LiveContextProvider提供,而liveProvider来自Refine组件的liveProviderprop;返回类型:类型签名NonNullableLiveProvider[publish]表示返回值类型等价于LiveProvider[publish],即(event: LiveEvent) void | undefined。结合 types.ts 中的LiveProvider定义 中publish是可选方法这一点可知,若未在Refine中配置liveProvider,或 provider 未实现publish,该 Hook 返回undefined,因此调用时建议用publish?.(...)做可选调用(内置 mutation 钩子正是这样处理的);单元测试印证:packages/core/src/hooks/live/usePublish/index.spec.ts 用renderHook挂载了一个带publishmock 的liveProvider,断言publish?.({ channel, date, payload, type })后 mock 被调用一次且参数与传入的LiveEvent完全一致,从测试层面验证了纯透传、无额外处理这一行为。usePublish与同目录下的useSubscription、useResourceSubscription、useLiveMode一起从 packages/core/src/hooks/live/index.ts 统一导出,构成 refine 实时能力的四个基础 Hook。五、内置能力:mutation 钩子在成功后自动 publish官方文档特别提示:refine 在 mutation 钩子内部使用usePublish,在 mutation 成功后发布事件。这一点在源码中得到完整印证——以下六个数据 Hook 均导入了usePublish,并在 mutation 的onSuccess回调中调用publish?.({...}):钩子发布事件 typeuseCreatecreateduseCreateManycreateduseUpdateupdateduseUpdateManyupdateduseDeletedeleteduseDeleteManydeleted以 packages/core/src/hooks/data/useCreate.ts 为例,Hook 内部先const publish usePublish();取出方法,随后在onSuccess中完成通知 → 失效查询 → 发布事件三步,publish?.({...})位于 第 222 行附近。各钩子发布的标准事件形态如下(摘自 Live Provider 文档):useCreate创建一条posts记录后:// 调用 mutate({ resource: posts, values: { title: New Post }, }); // 自动发布的事件 { channel: resources/posts, type: created, payload: { ids: [id-of-created-post] }, date: new Date(), }useCreateMany批量创建后:{ channel: resources/posts, type: created, payload: { ids: [id-of-new-post, id-of-another-new-post] }, date: new Date(), }useDelete/useDeleteMany删除后:{ channel: resources/posts, type: deleted, payload: { ids: [1, 2] }, date: new Date(), }useUpdate/useUpdateMany更新后:{ channel: resources/posts, type: updated, payload: { ids: [1, 2] }, date: new Date(), }这些事件会被订阅同一 channel 的其他useList/useTable/useOne/表单类钩子感知,配合liveMode: auto自动失效并重新拉取查询,实现一处修改、全局刷新的实时体验。六、最佳实践:客户端发布 vs 服务端发布官方文档对发布位置给出了明确的分层建议:1. 数据变更事件(should)从服务端发布。服务端在真实的数据操作完成时发布事件,能保证事件与数据库状态严格一致、且所有客户端都能收到,标准格式为:// 创建记录时 { channel: resources/${resource}, type: created, payload: { ids: [id] }, date: new Date(), } // 删除记录时 { channel: resources/${resource}, type: deleted, payload: { ids: [id] }, date: new Date(), } // 更新记录时 { channel: resources/${resource}, type: updated, payload: { ids: [id] }, date: new Date(), }2. 客户端发布(可,但需谨慎)。通过usePublish发布客户端事件时,官方 caution 指出:安全需要开发者自行保障——客户端拿到的凭证与身份可被伪造,不能把客户端发布的created/deleted等事件当作可信的数据变更来源。因此建议将usePublish用于:发布自定义业务事件(custom-channel-name 自定义type),驱动非数据类的实时交互;在无服务端发布条件的开发/演示环境(如useMockdataProvider)中,借助 mutation 钩子的自动 publish 模拟实时更新效果。3. 订阅侧配套。发布出去的事件需要订阅者才能生效,配套 Hook 为useSubscription(手动订阅任意 channel 并处理onLiveEvent)与受支持数据钩子的自动订阅;liveMode取值为auto(事件到达时自动失效相关查询)/manual(仅触发onLiveEvent回调,适合编辑表单等不希望数据被实时变更的场景)/off(关闭实时)。完整的 live provider 编写方法(Ably 示例、subscribe/unsubscribe参数表、支持订阅的钩子清单)见 Live Provider 文档。七、小结要点说明依据usePublish本质从LiveContext取出liveProvider.publish并原样返回usePublish 源码未配置 liveProvider 时返回undefined,调用需用publish?.(...)源码类型LiveProvider[publish]为可选方法事件结构channeltypepayload(建议含ids)date,类型为LiveEventtypes.ts自动发布useCreate/useCreateMany/useUpdate/useUpdateMany/useDelete/useDeleteMany在成功后自动 publish 对应事件useCreate.ts 等安全建议数据变更事件应由服务端发布,客户端发布需自行保障安全官方文档 caution单测验证传入的LiveEvent原样透传给 provider 的publish且仅调用一次index.spec.tsusePublish虽然只有十行实现,却是 refine 数据钩子自动广播 开发者自定义事件这套实时体系的客户端出口:理解了它与LiveEvent结构、liveMode及服务端发布规范的配合,你就能在 refine 3.x 项目中把任意实时服务(Ably、Supabase、Hasura 等)接入完整的数据实时同步链路。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考