深度解析:从钩子签名到执行、结果与错误处理)
Metabase 模块化嵌入 SDK 的 useAction 返回类型UseActionResult深度解析从钩子签名到执行、结果与错误处理【免费下载链接】metabaseThe easy-to-use open source Business Intelligence and Embedded Analytics tool that lets everyone work with data :bar_chart:项目地址: https://gitcode.com/GitHub_Trending/me/metabase导读UseActionResult是 Metabase 模块化嵌入 SDK 中useAction钩子的返回值类型它把「触发一个已有 Metabase Action写操作」这件事封装成一组 React 状态与回调execute发起 HTTP 请求、isExecuting暴露加载态、result与error保存最近一次响应与异常、reset重置状态。本文将围绕该类型签名逐字段展开结合仓库中的useAction文档docs/embedding/sdk/actions.md、TypeScript 类型定义片段与后端执行实现src/metabase/actions/execution.clj讲清楚参数泛型如何驱动判别式结果类型、何时会返回null、如何用in运算符收窄结果、以及错误对象中status、data.message、data.errors的真实语义。读完你将能正确地把useAction集成进嵌入应用并写出类型安全的按钮、表单与刷新联动逻辑。一、定位useAction钩子与它的返回值UseActionResult不是独立 API而是useAction钩子的返回类型。在嵌入应用中它的典型形态如下见 useAction 类型定义const { execute, isExecuting, result, error, reset } useAction TParameters, TKind // 可选——驱动带类型的 result 形状 (actionId);其类型签名定义于 docs/embedding/sdk/api/snippets/UseActionResult.mdfunction useActionTParameters, TKind( actionId: SdkActionId | null, ): UseActionResultTParameters, TKind;与查询类钩子如useMetabot等最大的不同是useAction不会在组件挂载时自动执行。它需要调用方在事件处理器按钮点击、表单提交中显式调用execute来触发。这与后端的「写操作必须由用户动作显式驱动」的语义一致——后端执行入口execute-action!也只在收到显式请求参数时才运行见 src/metabase/actions/execution.clj#L195-L225。二、类型参数TParameters与TKindUseActionResultTParameters, TKind携带两个泛型均由useAction透传类型参数约束含义TParametersextendsRecordstring, unknown描述传给execute的参数对象形状。键是 Action 参数的slug即 Action 编辑器里显示的名字不是内部 UUID。TKindextendsActionKind \| undefinedAction 的种类字面量驱动判别式result类型。省略时回退为AnyActionResult联合类型。ActionKind是面向调用方的五值联合见 ActionKind 类型type ActionKind create | update | delete | bulk | sql;从源码看这五个值映射到后端两套命名体系单行增删改对应后端的row/create、row/update、row/delete隐式 Actionlegacy-current映射见 src/metabase/actions/execution.clj#L113-L116bulk覆盖任何批量变体sql覆盖自定义 SQL Action后端query类型。典型调用示例出自 useAction 文档useAction{ name: string; email: string }, create(42);三、返回对象属性逐一解析UseActionResultTParameters, TKind共暴露五个字段见 UseActionResult 属性表属性类型说明errorActionExecuteError \| null最近一次抛出的错误已规范化为公开的ActionExecuteError形状无错误时为null。execute(parameters: TParameters) PromiseActionResultForKindTKind \| null用给定参数触发 Action。成功时返回响应体失败时抛出异常——同一个错误同时写入error状态供渲染期消费。当TKind省略时返回AnyActionResult可用key in r收窄。当actionId为null或 SDK 尚未初始化时不会发请求而是直接 resolve 为null。isExecutingboolean调用发起后、Promise resolve 前为true。常用于禁用触发按钮、防止双击。reset() void清空result与error。resultActionResultForKindTKind \| null最近一次响应体首次调用前与reset()之后为null。3.1execute的null分支与守卫execute返回Promise... | null是一个需要特别注意的设计当actionId为null或 SDK 尚未完成初始化时它不会发出任何请求直接 resolve 为null。官方文档的建议是如果这些情况在你的宿主应用中可能触达就用宿主侧守卫提前返回if (!actionId) return;否则应当把null结果纳入类型收窄路径。3.2result的判别式形状ActionResultForKindTKindresult的类型由ActionResultForKindTKind按TKind分发见 ActionResultForKind 定义传入create得到ActionResultForCreateupdate得到ActionResultForUpdate依次类推TKind省略undefined时回退为AnyActionResult联合。五种形状与后端响应一一对应TKindresult形状含义create{ created-row: Recordstring, RowValue }单行插入基础 Action返回被插入的行update{ rows-updated: readonly RowValue[] }单行更新返回受影响的主键delete{ rows-deleted: readonly RowValue[] }单行删除返回受影响的主键bulk{ success: boolean; rows-created?: number; rows-updated?: number; rows-deleted?: number }任意批量变体成功标志 可选计数sql{ rows-affected: number }自定义 SQL Action 返回受影响行数对应类型定义见 ActionResultForCreate、ActionResultForUpdate、ActionResultForDelete、ActionResultForBulk、ActionResultForSql。例如 SQL Action示例出自 typed-response.tsxconst { execute, result } useActionSetDiscountParameters, sql( SET_DISCOUNT_ACTION_ID, ); const onClick async () { await execute({ id: orderId, discount: 0.1 }); }; // result 被类型化为 { rows-affected: number } | null无需 cast const affected result?.[rows-affected];3.3 省略TKind时的联合收窄如果不提供TKindresult默认为AnyActionResult——五种响应体的联合见 AnyActionResult 定义。此时 TypeScript 知道结果是五种已知形状之一只是不知道具体是哪一种可用in运算符逐键收窄let summary Apply discount; if (result rows-affected in result) { // 此处 result[rows-affected] 类型为 number summary ${result[rows-affected]} rows affected; } else if (result created-row in result) { // 此处 result[created-row] 类型为 Recordstring, RowValue summary Row created; }联合默认值还能拦截误读如果类型系统无法证明result存在某个键会直接报编译错误而不是把result弱化为宽容的Recordstring, unknown。四、错误状态error与ActionExecuteErrorerror被类型化为ActionExecuteError | null读取时无需 castconst message error?.data?.message;ActionExecuteError的完整形状见 ActionExecuteError 类型type ActionExecuteError { data: { errors?: Recordstring, string; message?: string; }; isCancelled: boolean; status?: number; };属性类型说明data{ errors?: Recordstring, string; message?: string }错误载荷data.errors?Recordstring, string后端报告参数级校验失败时的逐字段映射{ slug: message }data.message?string对终端用户最有用的可操作诊断信息isCancelledboolean请求是否被取消status?number仅 HTTP 层失败4xx/5xx时存在传输层失败离线、中止、未收到 HTTP 响应时缺省关键语义status是可选字段HTTP 级失败时携带状态码传输层失败时不存在。判断时要写error.status ! undefined而非!error.status。data.errors与data.message的分工参数级校验失败时data.errors是按参数 slug 组织的映射整请求失败如外键约束{ message: Other rows refer to this row…, errors: {} }时它是空{}诊断信息放在data.message。这正是后端check-no-extra-parameters等校验路径以ex-info携带:message、:parameters、:destination-parameters等数据再被错误中间件规范化的结果见 src/metabase/actions/execution.clj#L98-L111 与 src/metabase/actions/execution.clj#L82-L96。SQL/驱动错误通常包含失败 SQLerror.data.message常以换行附上失败的 SQL 语句因此渲染时应使用white-space: pre-wrappre即可span会把换行折叠成一片文字。execute的「成功返回、失败抛出」与error状态双轨并存的机制意味着即使不在try/catch中捕获渲染期的错误提示也会出现。基本示例出自 basic.tsx同时展示了两条路径const onClick async () { try { await execute({ id: orderId, discount }); } catch { // 抛出的错误同样写入了 error 状态供下方渲染 } }; // ... {error ? ( pre style{{ whiteSpace: pre-wrap }} {error.data.message ?? Action failed.} /pre ) : null}官方建议原样展示错误消息不要替换成笼统的「Something went wrong」——原始 SQL/校验/权限错误才是用户修正输入的线索。五、实战参数传递与日期时区规范execute接收的参数对象以slug为键。参数支持 string、number、boolean日期传 ISO 8601 字符串示例出自 parameter-values.tsxawait execute({ name: Jane, // string 参数 age: 30, // number 参数 is_active: true, // boolean 参数 birth_date: 1995-04-22, // date 参数ISO 格式 created_at: 2024-01-15T10:00:00Z, // timestamp 参数ISO 带 Z 表示 UTC });日期时区有三个要点详见 actions.md 的日期小节无时区的TIMESTAMP列发送不带时区偏移的 ISO 值或带Z后缀。偏移值如2024-01-15T10:00:0005:00通常会被数据库驱动转换为 UTC墙上时钟会偏移精确处理需按仓库确认。TIMESTAMP WITH TIME ZONE列偏移被保留为同一时刻DATE列则与时区无关。浏览器本地日期选择器返回的是本地时区值发送前先归一化示例出自 date-picker.tsxconst picked new Date(datePickerValue); await execute({ created_at: picked.toISOString() }); // 始终带 Z 后缀字符串无法解析时数据库驱动会抛出异常消息通过error.data.message暴露。六、成功后的数据刷新没有自动刷新useAction触发成功后不会自动刷新任何查询组件界面上的数据可能过期。官方推荐用refreshKey强制问题组件重挂载来重跑查询示例出自 with-refresh.tsxfunction OrdersScreen() { const [refreshKey, setRefreshKey] useState(0); return ( div InteractiveQuestion key{refreshKey} questionId{ORDERS_QUESTION_ID} / MarkAllShippedButton onShipped{() setRefreshKey((key) key 1)} / /div ); } function MarkAllShippedButton({ onShipped }: { onShipped: () void }) { const { execute, isExecuting } useActionMarkShippedParameters( MARK_SHIPPED_ACTION_ID, ); const onClick async () { await execute({ id: 1 }); onShipped(); // 仅在 Action 成功后刷新让问题组件对变更后的行重新查询 }; // ... }如果单个 Action 使多个视图失效让所有依赖的问题组件共享同一个refreshKey一次状态递增即可同时重挂载、统一重查询示例出自 parallel-refresh.tsxawait execute({ id: orderId }); // 一次状态递增重挂载所有依赖视图使它们一起刷新 setRefreshKey((key) key 1);注意不要用result驱动数据状态响应体用于确认行数、插入行的主键等可用来弹 toast 或跳转详情但屏幕数据仍需重新读取数据源。七、actionId的取值与执行边界actionId的类型是SdkActionId见 SdkActionId 类型type SdkActionId number | SdkEntityId;即 Action 的数字 id 或entity_id字符串也可以是null此时execute直接 resolvenull。获取方式在 Metabase 中打开 Action 编辑器从 URL 复制数字 id或通过GET /api/action获取。后端执行入口同时支持按 id 查询 Action 并校验权限见 src/metabase/actions/execution.clj#L195-L225隐式 Action 会校验表必须有唯一主键、参数 slug 唯一且无多余参数src/metabase/actions/execution.clj#L118-L171。需要强调的执行边界基础 CRUD 与自定义 SQL Action 均受支持HTTP 类型 Action 不支持。官方明确建议始终通过useAction触发——在沙箱化嵌入环境中直接用fetch调用POST /api/action/:id/execute可能被拦截后端也在公开端点执行时对 HTTP Action 显式抛 403见 src/metabase/actions/execution.clj#L199-L202。八、要点速查useAction不会自动执行在事件处理器中调用execute用if (!user.canEdit) return;这类宿主侧分支做条件门控。isExecuting用于禁用触发按钮、防止双击重复提交。知道 Action 种类就用TKind第二个泛型换取精确的result类型不知道就省略用key in result收窄联合。actionId为null或 SDK 未初始化时executeresolve 为null不发起请求。错误统一从error.data.message读取可操作诊断参数级错误在error.data.errors按 slug 映射传输层失败时无status。Action 成功后必须手动刷新数据refreshKey重挂载问题组件SDK 不会自动刷新。相关资源钩子与返回类型 useAction、UseActionResult类型家族 ActionKind、AnyActionResult、ActionResultForKind、ActionExecuteError完整使用指南 docs/embedding/sdk/actions.md可直接运行的示例 basic.tsx、typed-response.tsx、with-refresh.tsx、parallel-refresh.tsx、parameter-values.tsx、date-picker.tsx后端执行实现 src/metabase/actions/execution.clj【免费下载链接】metabaseThe easy-to-use open source Business Intelligence and Embedded Analytics tool that lets everyone work with data :bar_chart:项目地址: https://gitcode.com/GitHub_Trending/me/metabase创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考