Refine v5 useLogList Hook 实战指南:审计日志列表查询、底层原理与边界情况
2026/9/11 7:47:36 网站建设 项目流程

Refine v5 useLogList Hook 实战指南:审计日志列表查询、底层原理与边界情况

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

导读

useLogList是 Refine 提供的审计日志(Audit Log)查询 Hook,用于在应用中获取并筛选审计事件列表,其底层封装了auditLogProviderget方法。本文以 useLogList 官方文档 为主体,结合 @refinedev/core 源码 与单元测试,深入讲解其调用链、query key 生成规则、参数/返回值语义以及queryOptions的覆盖行为,帮助你为合规审计、操作追溯等场景快速搭建可靠的日志列表。


一、useLogList 是什么

当你的应用需要展示"某个资源发生过哪些操作、由谁执行"这类审计事件时,就可以使用useLogList。它在内部调用auditLogProvider.get方法拉取事件列表,并复用@tanstack/react-queryuseQuery提供缓存、重试、状态管理等能力。

Refine 的审计日志体系由 auditLogProvider 统一承载,它包含三个方法:

  • create:记录一条审计事件;
  • get:返回事件列表;
  • update:更新一条审计事件。

useLogList只负责与get对接,useLog则负责与create/update对接。两者相辅相成:写操作走useLog(或自动化的数据变更 Hook),读操作走useLogList

二、快速上手

最基本的用法只需传入resource

import { useLogList } from "@refinedev/core"; const postAuditLogResults = useLogList({ resource: "posts", });

其中postAuditLogResults就是useQuery的返回结果,你可以像使用普通 react-query 查询一样访问dataisLoadingisFetchederrorrefetch等字段:

const { data, isLoading, error, refetch } = useLogList({ resource: "posts", }); if (isLoading) return <div>加载审计日志中...</div>; if (error) return <div>加载失败:{error.message}</div>; return ( <ul> {data?.map((event: any) => ( <li key={event.id}> [{event.action}] {JSON.stringify(event.data)} </li> ))} </ul> );

需要说明的是:useLogList必须在使用前将auditLogProvider传入<Refine>组件,否则get方法不存在,查询会被禁用(详见下文"enabled 默认行为"一节)。

三、底层实现:一次查询的完整调用链

阅读 useLogList 源码 可以看到它的完整逻辑:

export const useLogList = < TQueryFnData = any, TError extends HttpError = HttpError, TData = TQueryFnData, >({ resource, action, meta, author, queryOptions, }: UseLogProps<TQueryFnData, TError, TData>): UseQueryResult<TData, TError> => { const { get } = useContext(AuditLogContext); const { keys } = useKeys(); const queryResponse = useQuery<TQueryFnData, TError, TData>({ queryKey: keys() .audit() .resource(resource) .action("list") .params(meta) .get(), queryFn: () => get?.({ resource, action, author, meta, }) ?? Promise.resolve([]), enabled: typeof get !== "undefined", ...queryOptions, retry: false, meta: { ...queryOptions?.meta, ...getXRay("useLogList", resource), }, }); return queryResponse; };

整个调用链可以拆解为四个关键环节:

1. 从 AuditLogContext 取出get

useLogList通过useContext(AuditLogContext)获取auditLogProvider暴露的get方法,而不是直接读取props。这意味着auditLogProvider是通过<Refine auditLogProvider={...}>以全局上下文的方式注入的,任何层级的组件都能直接调用useLogList,无需层层透传。

2. 自动生成稳定且可缓存的 query key

查询键通过useKeys()构建:

keys().audit().resource(resource).action("list").params(meta).get()

它固定了action为字符串"list"(注意:这里的"list"是查询动作标识,不是审计事件类型),并把resourcemeta纳入 key 的组成。这意味着:

  • 相同resource+ 相同meta的查询会命中同一份缓存,切页或重复渲染时直接复用;
  • meta中的筛选条件(如记录 id)变化时,key 随之变化,react-query 会自动发起新查询。

具体的 key 生成实现位于 keys helper 的audit()方法中。

3. 调用get并处理"未配置 provider"的兜底

queryFn中使用了可选调用get?.(...),当auditLogProvider未配置时getundefined,此时返回Promise.resolve([])(空数组),不会抛出异常。与此同时,enabled: typeof get !== "undefined"会在 provider 缺失时自动禁用查询,避免无意义的请求。也就是说:即使忘记配置 provider,组件也不会崩溃,只是拿不到数据

4. 注入 DevTools 追踪信息

最终传给useQuerymeta会被合并进getXRay("useLogList", resource)生成的追踪信息,供 Refine DevTools 的 X-Ray 面板展示 Hook 调用关系与资源名称,便于调试。

四、参数详解(Properties)

useLogList接受以下属性(与源码中的 UseLogProps 类型一一对应):

Property类型默认值说明
resource(必填)string从路由读取的 action要查询审计日志的资源名称,例如"posts""products"
actionstring按操作类型筛选,例如"create""update""delete"
authorRecord<string, any>按操作者筛选,例如{ id: 1 }{ username: "admin" }
metaRecord<string, any>附加元数据,通常用于携带记录 id 等筛选条件
queryOptionsUseQueryOptions<TQueryFnData, TError, TData>透传给useQuery的选项,可覆盖 queryKey、queryFn 等

各参数的实战含义

  • resource:必填项,决定查询哪一类资源的审计事件。虽然表格中默认值为"从路由读取的 action",但源码将其声明为必填,建议始终显式传入。
  • action:用于只筛选某一种操作,例如仅查看删除记录。若省略,则由auditLogProvider.get自行决定返回范围。
  • meta:最常见的用法是携带id,查询"某一条记录的全部审计历史"。例如{ id: 1 }。源码会把meta原样传给get,同时它也会参与 query key 的生成。
  • author:用于按操作者过滤,例如展示"某个管理员执行过的所有操作"。
  • queryOptions:react-query 的完整选项集合,覆盖场景见下文第六节。

五、泛型参数与返回值

Type Parameters

参数描述类型默认值
TQueryFnData查询函数返回的数据类型,继承自BaseRecordBaseRecordBaseRecord
TError自定义错误类型,继承自HttpErrorHttpErrorHttpError
TDataselect函数返回的数据类型,继承自BaseRecord;未指定时取TQueryFnDataBaseRecordTQueryFnData

使用示例:

import type { HttpError } from "@refinedev/core"; type PostAuditEvent = { id: number; action: string; data: Record<string, any>; author?: { name?: string }; }; const { data } = useLogList<PostAuditEvent[], HttpError>({ resource: "posts", });

Return values

描述类型
react-queryuseQuery返回结果UseQueryResult<{ data: TData; }>

返回值包含dataisLoadingisErrorerrorisFetchedrefetchisFetching等标准字段,其中dataget方法 resolve 出的审计事件数组。注意源码中返回类型实际为UseQueryResult<TData, TError>TData即整个查询数据),与文档表格中标注的{ data: TData }相对应的是 react-query 内部对结果对象的包裹语义。

六、通过 queryOptions 精确控制查询行为

queryOptions会被展开合并进useQuery,实现细粒度控制。结合单元测试可以确认以下两种核心覆盖行为。

覆盖 queryKey

默认 query key 由keys().audit()...自动生成;如果你需要手动指定(例如与应用其他查询共享缓存),可以直接传入:

useLogList({ resource: "posts", queryOptions: { queryKey: ["foo", "bar"], }, });

对应的测试用例should override queryKey with queryOptions.queryKey验证了传入后缓存查询键被替换为["foo", "bar"]

覆盖 queryFn

默认的queryFn调用get方法;当你希望完全接管数据获取逻辑(例如先从本地缓存读取、失败再请求接口)时:

useLogList({ resource: "posts", queryOptions: { queryFn: async () => { // 自定义获取逻辑 return customFetchAuditLogs(); }, }, });

测试用例should override queryFn with queryOptions.queryFn验证了覆盖后get方法不会被调用,且自定义queryFn正常执行。

此外还有两个源码层面的固定行为需要留意:

  • retry: false:即使你通过queryOptions传入retry,也会被展开顺序中靠后的retry: false覆盖。也就是说 useLogList 的查询默认不重试,请求失败立即进入error状态。这是有意的设计——审计查询失败通常需要立刻暴露给用户,而不是静默重试。
  • meta合并:你传入的queryOptions.meta会与 DevTools 的getXRay追踪信息合并,两者不会互相覆盖。

七、完整实战:auditLogProvider.get + useLogList

要让useLogList真正返回数据,先按审计日志 Provider 文档实现get方法。例如按资源名与记录 id 查询事件:

import type { AuditLogProvider } from "@refinedev/core"; export const auditLogProvider: AuditLogProvider = { get: async (params) => { const { resource, meta, action, author } = params; const response = await fetch( `https://example.com/api/audit-logs/${resource}/${meta?.id}`, { method: "GET", }, ); const data = await response.json(); return data; }, // create / update 按需实现 };

再通过<Refine>注入:

import { Refine } from "@refinedev/core"; import { auditLogProvider } from "./audit-log-provider"; const App = () => ( <Refine /* ... */ auditLogProvider={auditLogProvider} /> );

然后在任意页面组件中查询"某篇文章(id=1)的全部操作记录":

import { useLogList } from "@refinedev/core"; export const PostAuditLog = ({ postId }: { postId: number }) => { const { data, isLoading, isError, error } = useLogList({ resource: "posts", meta: { id: postId }, // 查询该记录的全部审计事件 }); if (isLoading) return <p>加载中...</p>; if (isError) return <p>出错了:{error?.message}</p>; return ( <table> <thead> <tr> <th>事件类型</th> <th>操作者</th> <th>变更内容</th> </tr> </thead> <tbody> {data?.map((log: any) => ( <tr key={log.id}> <td>{log.action}</td> <td>{log.author?.name ?? "-"}</td> <td> <pre>{JSON.stringify(log.data ?? log.previousData, null, 2)}</pre> </td> </tr> ))} </tbody> </table> ); };

postId变化时,meta改变会生成新的 query key,自动触发重新查询——这正是审计日志"按记录查看历史"场景的标准做法。

组合筛选:action + author

如果需要"只看某个管理员对某篇文章的删除操作",可以同时传入多个筛选条件:

const { data } = useLogList({ resource: "posts", action: "delete", author: { username: "admin" }, meta: { id: postId }, });

筛选的最终解释权在auditLogProvider.get的实现中:Refine 只负责把actionauthormeta原样透传(源码证据),服务端如何组合这些条件取决于你的后端 API。

八、测试验证:源码如何保障行为正确

仓库为useLogList提供了专门的单元测试 useLogList/index.spec.ts,覆盖了三条核心行为:

  1. 透传属性useLogList({ resource: "posts", action: "list", meta: { id: 1 } })会以相同参数调用auditLogProvider.get,且返回的dataget的 resolve 值严格一致;
  2. queryKey 覆盖:传入queryOptions.queryKey后,react-query 缓存中只存在自定义 key;
  3. queryFn 覆盖:传入queryOptions.queryFn后,get不再被调用,改由自定义函数取数。

这些测试同时验证了useLogList与 react-query 的集成行为,是你在二次封装或排查"为什么没触发请求"问题时的重要参考。

九、使用建议与边界情况总结

  • provider 未配置时:查询自动禁用(enabled: false),data保持undefined,不会报错;请检查<Refine>是否传入了auditLogProvider
  • 不要依赖重试retry: false是写死的,失败即失败,请在前端做好错误提示与手动refetch
  • 查询键的语义:默认 key 中的action("list")表示"列表查询"这一动作,与审计事件类型(create/update/delete)无关,两者不要混淆。
  • 与 useLog 的分工:写审计事件用useLog(底层调create/update),读审计事件用useLogList(底层调get)。Refine 的数据变更 Hook(如useCreateuseUpdateuseDelete等)在成功后会自动调用create记录事件,无需你手动埋点。
  • 按变更类型裁剪日志:如果只想记录部分操作,可在资源配置中使用meta.audit白名单,例如meta: { audit: ["create"] }表示只有create事件会被记录(详见 audit-log-provider 文档)。
  • 安全边界:官方文档明确建议审计日志在服务端创建,因为客户端写入的数据可被篡改,不能作为可信的事实来源。get查询侧的筛选参数也应视为"用户输入",在后端做好权限校验。

结语

useLogList是 Refine 审计日志体系中的读取入口:它把auditLogProvider.get包装成声明式的 react-query 查询,自动处理缓存键、provider 缺失兜底与 DevTools 追踪。掌握它的参数语义、query key 生成规则和queryOptions覆盖行为,你就可以在合规审计、操作追溯等场景中快速构建出健壮、可缓存的日志列表,并与 Refine 的数据变更 Hook 形成完整的"自动记录 + 便捷查询"闭环。

【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards & B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询