ARTICLE DETAIL

资讯详情

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

Refine 中用 useUpdate 钩子更新记录:从实现 DataProvider.update 到乐观更新与查询失效机制

Refine 中用 useUpdate 钩子更新记录:从实现 DataProvider.update 到乐观更新与查询失效机制 Refine 中用 useUpdate 钩子更新记录从实现 DataProvider.update 到乐观更新与查询失效机制【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine本文基于 Refine 官方教程中“Updating a Record”一节完整演示如何在数据提供层实现update方法、如何用useUpdate钩子更新单条 API 记录并结合packages/core源码剖析该钩子背后的乐观更新、智能失效Smart Invalidations与可撤销undoable变更机制。读完本文你不仅能复刻教程中的完整操作还能理解useUpdate在 TanStack Query 之上封装了哪些自动化行为。一、更新记录的两步走update方法与useUpdate钩子Refine 的数据交互遵循“数据提供层 数据钩子”的分层模型。要更新一条记录需要完成两件事在 DataProvider 中实现update方法——这是所有更新操作的统一入口在组件中调用useUpdate钩子——它会最终路由到 DataProvider 的update方法。useUpdate钩子及全部数据钩子的源码位于 useUpdate.ts其 JSDoc 明确说明它是 “react-querysuseMutation的修改版专用于 update 变更”并以传入Refine的 DataProvider 的update方法作为变更函数mutation function。二、实现update方法update方法接受四个属性属性说明resource要更新的实体资源名id要更新的记录 IDvariables发送到 API 的数据对象meta传递给钩子的附加数据如筛选、分页等查询上下文Refine 自带的 fake APIhttps://api.fake-rest.refine.dev要求通过PATCH /products/:id端点更新记录。因此我们在>import type { DataProvider } from refinedev/core; const API_URL https://api.fake-rest.refine.dev; export const dataProvider: DataProvider { getOne: async ({ resource, id, meta }) { const response await fetch(${API_URL}/${resource}/${id}); if (response.status 200 || response.status 299) throw response; const data await response.json(); return { data }; }, update: async ({ resource, id, variables }) { const response await fetch(${API_URL}/${resource}/${id}, { method: PATCH, body: JSON.stringify(variables), headers: { Content-Type: application/json, }, }); if (response.status 200 || response.status 299) throw response; const data await response.json(); return { data }; }, getList: () { throw new Error(Not implemented); }, /* ... */ };从源码结构看DataProvider 接口中update是必选方法与可选的updateMany?、getMany?不同签名为update: TData extends BaseRecord BaseRecord, TVariables {}( params: UpdateParamsTVariables, ) PromiseUpdateResponseTData;即update返回PromiseUpdateResponseTData其中UpdateResponse定义为{ data?: TData }。这也解释了教程代码为什么统一用return { data }包裹响应体——这是钩子约定读取结果的方式。三、使用useUpdate钩子实现update方法后就可以调用useUpdate更新单条记录。教程中我们新建EditProduct组件先用useOne拉取待更新记录再用useUpdate发起更新src/pages/products/edit.tsximport { useOne, useUpdate } from refinedev/core; export const EditProduct () { const { result, query: { isLoading }, } useOne({ resource: products, id: 123 }); const { mutate, mutation: { isPending: isUpdating }, } useUpdate(); if (isLoading) { return divLoading.../div; } const updatePrice async () { await mutate({ resource: products, id: 123, values: { price: Math.floor(Math.random() * 100), }, }); }; return ( div divProduct name: {result?.name}/div divProduct price: ${result?.price}/div button onClick{updatePrice}Update Price/button /div ); };然后在src/App.tsx中把EditProduct挂到Refine /组件内import { Refine } from refinedev/core; import { dataProvider } from ./providers/data-provider; import { ShowProduct } from ./pages/products/show; import { EditProduct } from ./pages/products/edit; export default function App(): JSX.Element { return ( Refine dataProvider{dataProvider} {/* ShowProduct / */} EditProduct / /Refine ); }此时页面会显示产品名称与价格点击 “Update Price” 按钮后价格即被更新。useUpdate的入参类型UpdateParams中除教程用到的resource/id/values外还支持mutationMode、undoableTimeout、onCancel、meta、dataProviderName、invalidates、optimisticUpdateMap等选项这些将在下一节结合源码展开。四、源码深潜useUpdate到底做了什么useUpdate.ts 中useUpdate内部构建了一个 TanStack Query 的useMutation并分四个阶段工作4.1 mutationFn参数校验与 DataProvider 调用在 mutationFn 中id、values、resourceName三者缺一即抛出预定义错误missingIdError、missingValuesError、missingResourceError。随后钩子把values重命名为variables调用 DataProviderdataProvider(...).updateTData, TVariables({ resource: resource.name, id, variables: values, meta: combinedMeta, });注意resource会先经过select(resourceName)解析——这意味着传入identifier资源标识符而非nameAPI 资源名时也能正确路由到对应 DataProvider测试 useUpdate.spec.tsx 中专门有 “when passingidentifierinstead ofname” 的 describe 块验证这一点。4.2 onMutate默认开启的乐观更新这是教程没有展开、但源码里最核心的部分。在 onMutate 阶段先通过queryClient.getQueriesData快照保存该资源所有相关查询的当前数据previousQueries取消正在进行的请求cancelQueries只要 mutation 模式不是pessimistic就按optimisticUpdateMap配置把values直接合并进缓存——默认配置为{ list: true, many: true, detail: true }即列表查询list、多记录查询many、详情查询one全部即时更新if (optimisticUpdateMap.list) { queryClient.setQueriesData({ queryKey: resourceKeys.action(list)... }, (previous) { // 将 values 合并到 previous.data 中 id 匹配的记录上 }); }因此界面在 API 返回前就显示新值。optimisticUpdateMap的每一项都可以传自定义映射函数OptimisticUpdateMapType如(previous, values, id) 新数据也可以传false关闭对应通道的乐观更新——这些行为在 useUpdate.spec.tsx 的 “when passingoptimisticUpdateMap” 测试块中有完整断言。4.3 onSettled智能失效Smart Invalidations教程提示框中提到“用useUpdate更新价格后之前调用的useOne会自动失效”。源码依据就在 onSettledconst { invalidates invalidatesFromProps ?? [list, many, detail] } variables; invalidateStore({ resource: identifier, dataProviderName: ..., invalidates, id, });无论成功或失败钩子都会失效该资源下list/many/detail三类查询可通过invalidates参数收窄范围触发 TanStack Query 重新拉取从而保证屏幕数据始终是最新的——这正是教程中“Smart Invalidations”提示的底层实现。4.4 onSuccess 与 onError发布事件、审计日志与回滚onSuccessL467-L563除了弹出默认的 “Successfully updated xxx” 成功通知还会通过publish向resources/{resource}频道发布updated实时事件供 LiveProvider 使用并调用log.mutate记录变更前后的数据供审计日志使用。onSuccess里还会从旧缓存中抽取被修改字段的原值作为previousData一并写入日志。onErrorL564-L613若存在onMutate保存的previousQueries快照会把每个查询的数据逐条恢复为变更前状态若是mutationCancelledundoable 模式被用户取消则静默处理否则弹出 “Error when updating xxx (status code: ...)” 错误通知。测试用例 “should work with optimistic update” 完整印证了这条链路先用mutationMode: optimistic发起更新断言useOne/useList/useMany的结果立刻变为新标题再让 API 以 500ms 延迟后报错断言三个查询的结果回滚为初始标题见 useUpdate.spec.tsx。五、三种变更模式Mutation Mode源码中mutationMode支持三种取值缺省时从Refine的 mutation 上下文useMutationMode继承模式行为optimistic默认请求发出前立即用新值更新缓存失败后回滚pessimistic等待 API 成功响应后才失效并重取缓存不会被提前修改undoable不立即请求而是把变更加入可撤销队列在undoableTimeout毫秒内用户可取消onCancel拿到cancelMutation函数超时后自动执行真实请求undoable的实现见 mutationFn 中的 updatePromise它把真实请求包装成doMutation连同cancelMutation一起通过notificationDispatch分发给通知层从而在 UI 上呈现带撤销按钮的提示。测试文件中对应 “should work with undoable update” 用例。六、钩子返回值与错误边界useUpdate最终返回L655-L660return { mutation: mutationResult, // 完整的 react-query useMutation 结果isPending/isError/isSuccess... mutate: handleMutation, // variables 参数可省略的 mutate mutateAsync: handleMutateAsync, overtime: { elapsedTime }, // 加载超时计时useLoadingOvertime };mutate/mutateAsync被包装了一层使得resource/id/values既可以在钩子入参中给也可以在mutate()调用时给“with props” 与 “with params” 两种用法在 useUpdate.spec.tsx 中各有一整组 describe 块验证overtime.elapsedTime配合overtimeOptions可用于实现“加载超过 N 秒就提醒用户”的体验缺失参数时抛出的错误信息如 “[useUpdate]:idis not defined but is required in edit and clone actions”对排查“为什么我的 update 没生效”非常有用注意id: 0、id: 等合法值不会误报——测试 “should not throw error when id 专门覆盖了这一边界。七、小结这篇教程展示了 Refine 更新记录的完整闭环DataProvider 的update方法负责把resource/id/variables/meta翻译成一次PATCH请求useUpdate钩子则在其上叠加了参数校验、乐观更新、失败回滚、跨查询智能失效、实时事件发布与审计日志记录。理解 packages/core/src/hooks/data/useUpdate.ts 与 packages/core/src/hooks/data/useUpdate.spec.tsx 这两份文件后再遇到useList、useCreate、useDelete等兄弟钩子同目录 hooks/data你会看到几乎一致的架构mutationFnonMutate乐观更新 onSettled失效 onSuccess/onError副作用只是查询键与数据形状不同。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表