十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Metabase Embedding SDK 的 useAction Hook 实战:在嵌入式应用中触发数据写入操作

Metabase Embedding SDK 的 useAction Hook 实战:在嵌入式应用中触发数据写入操作 Metabase Embedding SDK 的 useAction Hook 实战在嵌入式应用中触发数据写入操作【免费下载链接】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本指南讲解 Metabase Modular Embedding SDKReact 版中useActionHook 的完整用法如何让嵌入的应用在按钮点击或表单提交时触发 Metabase Action如何正确传递参数、解析类型化返回结果、处理错误以及为什么执行成功后必须主动刷新数据。读完本文你将能够在自己的 React 应用中安全、可靠地通过 SDK 调用 Metabase 的 BasicCRUD与自定义 SQL 两类 Action。useAction 是什么把 Metabase Action 变成 React HookMetabase 的 Actions 是基于参数化 SQL 的写入型实体用于把数据写回数据库例如给订单打折扣、把客户标记为 VIP、删除冗余数据。在嵌入场景下SDK 提供了useActionHook让你在宿主应用中像调用普通函数一样触发这些 Action。useAction的核心职责有三点封装 HTTP 请求自动向POST /api/action/:id/execute发起调用你无需手写fetch暴露加载与错误状态通过isExecuting、error等 React state 驱动 UI类型化参数与结果用泛型声明参数形状并按 Action 的 kind 自动推导result的类型。支持范围Basic CRUD Action单行 create / update / delete 及其 bulk 变体与自定义 SQL Actionquery类型均可触发HTTP 类型 Action 不支持后端在 src/metabase/actions_rest/api.clj 中对:http类型直接抛出 HTTP actions are not supported.。始终通过useAction触发不要绕过官方文档明确警告直接调用POST /api/action/:id/execute例如用fetch在沙箱化嵌入上下文中可能被拦截。SDK 的 Hook 会携带正确的鉴权上下文这是推荐且受支持的唯一入口。Hook 签名与返回状态const { execute, isExecuting, result, error, reset } useAction TParameters, TKind // 可选——驱动类型化 result 的形状 (actionId);参数与返回值逐项说明成员类型与说明actionIdAction 的数字 id、entity_id字符串或null。数字 id 可在 Metabase 中打开 Action 编辑器从 URL 复制类型定义见 SdkActionIdnumber \| SdkEntityIdTParameters描述传给execute的参数对象的 TypeScript 类型键是 Action 参数的slugAction 编辑器中显示的名字而非内部 UUIDTKind可选Action 的 kind 字面量create、update、delete、bulk或sql传入后result获得对应的单一形状省略时result默认为所有可能响应体的联合类型AnyActionResultexecute(parameters)在事件处理器中调用以触发 Action。Hook不会在挂载时自动执行。成功时 resolve 为响应体失败时 throw若actionId为null或 SDK 尚未初始化则 resolve 为null不发请求。可await也可 fire-and-forgetisExecuting调用与完成之间为true可用于禁用触发按钮、防止双击result最近一次响应体首次调用前与reset()之后为nullerror最近一次抛出的错误ActionExecuteError \| nullreset()清空result与error完整 API 类型可参考 useAction 与 UseActionResult。注意它与查询类 Hook 不同useAction不依赖挂载自动执行若需条件性门控应在事件处理器中分支如if (!user.canEdit) return;后再调用execute。最小示例一个触发自定义 SQL Action 的按钮下面这个按钮调用一个自定义 SQL Action给订单应用折扣。完整源码见 basic.tsximport { useState } from react; import { MetabaseProvider, defineMetabaseAuthConfig, useAction, } from metabase/embedding-sdk-react; const authConfig defineMetabaseAuthConfig({ metabaseInstanceUrl: https://your-metabase.example.com, }); // Action 的数字 identity_id 字符串亦可。 // 打开 Action 编辑器从 URL 复制或通过 GET /api/action 获取。 const SET_DISCOUNT_ACTION_ID 42; // 声明 Action 期望的参数形状。键是参数的 slug。 type SetDiscountParameters { id: number; discount: number; }; function SetDiscountButton({ orderId }: { orderId: number }) { const { execute, isExecuting, result, error } useActionSetDiscountParameters(SET_DISCOUNT_ACTION_ID); const [discount, setDiscount] useState(0.1); const onClick async () { try { await execute({ id: orderId, discount }); } catch { // 抛出的错误同样会写入 error state无需在这里处理。 } }; return ( div label Discount:nbsp; input typenumber step0.05 value{discount} onChange{(e) setDiscount(Number(e.target.value))} / /label button onClick{onClick} disabled{isExecuting} {isExecuting ? Applying… : Apply discount} /button {result ? spanDone./span : null} {error ? ( pre style{{ whiteSpace: pre-wrap }} {error.data.message ?? Action failed.} /pre ) : null} /div ); } export default function App() { return ( MetabaseProvider authConfig{authConfig} SetDiscountButton orderId{1} / /MetabaseProvider ); }要点拆解disabled{isExecuting}在请求进行中禁用按钮避免重复点击造成重复写入try/catch与 error state 双保险execute既 throw 又把同一错误写入errorstate因此即使没有try/catch渲染期的错误提示也会出现prewhite-space: pre-wrap用于原样展示多行错误消息详见错误处理一节。参数键与值类型按 slug 传参参数必须用 slug 作为键。参数的显示名name如Discount无效必须用 slug如discount。后端在 src/metabase/actions_rest/api.clj 的remap-parameter-keys中会按:id/:slug顺序解析传入键这也印证了 slug 是前端含 SDK 与 Action 编辑器 UI与后端约定的键形式。支持的参数值类型可传字符串、数字、布尔参数日期用 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 });日期与时区针对不同的目标列类型传值规则不同示例见 date-picker.tsxTIMESTAMP无时区传不带时区偏移的 ISO 值或带Z后缀的值await execute({ created_at: 2024-01-15T10:00:00Z }); // 10:00 UTC — 存储为 10:00带偏移的值会被驱动转换如2024-01-15T10:00:0005:00通常被数据库驱动转换为 UTC墙钟时间会偏移上例最终存储为05:00:00。具体行为因数据仓库而异若对时区精度敏感请核对所用驱动TIMESTAMP WITH TIME ZONE偏移被保留为同一时刻DATE时区无关。浏览器本地日期选择器的归一化datetime-local等控件通常返回用户本地时区的值发送前应归一化const picked new Date(datePickerValue); await execute({ created_at: picked.toISOString() }); // 始终带 Z 后缀若字符串无法解析数据库驱动会抛出异常错误消息经由error.data.message呈现见错误处理。类型化响应用 TKind 驱动 result 形状Action 的kind决定result的形状。将其作为第二个泛型传给useActionresult自动获得对应类型Action kind覆盖范围result形状create单行插入Basic Action{ created-row: Recordstring, RowValue }update单行更新{ rows-updated: readonly RowValue[] }delete单行删除{ rows-deleted: readonly RowValue[] }bulk任意批量变体bulk create/update/delete{ success: boolean; rows-created?: number; rows-updated?: number; rows-deleted?: number }sql自定义 SQL Action{ rows-affected: number }类型定义见 ActionKind各形状见 ActionResultForCreate、ActionResultForUpdate、ActionResultForDelete、ActionResultForBulk、ActionResultForSql 以及联合类型 ActionResultForKind 与 AnyActionResult。已知 kind 时的写法见 typed-response.tsxconst { execute, result } useActionSetDiscountParameters, sql( SET_DISCOUNT_ACTION_ID, ); // result 的类型为 { rows-affected: number } | null — 无需类型断言 const affected result?.[rows-affected];读取 result多数场景其实不需要读取result见下一节必须刷新数据。若确实要读且预先知道结果形状务必指定TKind。若不传TKindresult默认为AnyActionResult——所有可能响应体的联合类型。TypeScript 只知道它是五种已知形状之一此时可用in操作符收窄const { execute, result } useActionSetDiscountParameters( SET_DISCOUNT_ACTION_ID, ); 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拥有某个键会直接报编译错误而不是等到运行时。Action 成功后必须刷新数据没有自动刷新机制。Action 成功后UI 中任何可能被其更改的数据都必须手动刷新否则界面显示的是陈旧数据。单视图刷新用 refreshKey 重挂载在 state 中保存一个refreshKey将其作为 Question 的keyAction 成功后自增。新的key使 Question 全新挂载从而重新执行查询见 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 }); // 只在 Action 成功后再刷新Question 才会针对已变更的行重新查询 onShipped(); }; return ( button onClick{onClick} disabled{isExecuting} {isExecuting ? Shipping… : Mark order as shipped} /button ); }多视图并行刷新共用一个 refreshKey若单个 Action 使多个视图失效让所有依赖的 Question 共用同一个refreshKey一次状态自增即可整体重挂载、一起重新查询见 parallel-refresh.tsxconst { execute, isExecuting } useActionMarkShippedParameters( MARK_SHIPPED_ACTION_ID, ); const onClick async () { await execute({ id: orderId }); // 一次状态自增重挂载所有依赖视图一起刷新 setRefreshKey((key) key 1); }; return ( div InteractiveQuestion key{list-${refreshKey}} questionId{ORDERS_LIST_QUESTION_ID} / InteractiveQuestion key{stats-${refreshKey}} questionId{ORDERS_STATS_QUESTION_ID} / button onClick{onClick} disabled{isExecuting} {isExecuting ? Shipping… : Mark order as shipped} /button /div );不要用result直接驱动数据状态。响应体仅用于确认行数、插入行的主键等可用于 toast 提示或详情页导航但屏幕上数据的更新仍必须重新读取数据源。原因是 Action 返回的是写操作结果而非新数据把响应体当数据源会导致界面与数据库不一致。错误处理ActionExecuteError 的规范化形状Hook 会把底层网络客户端抛出的任何错误规范化为清晰的对外形状并将error类型化为ActionExecuteError | null定义见 ActionExecuteErrortype ActionExecuteError { data: { errors?: Recordstring, string; message?: string; }; isCancelled: boolean; status?: number; };各字段语义status可选HTTP 层失败4xx / 5xx时存在传输层失败离线、请求被中止、未收到 HTTP 响应时缺失data.message对终端用户最具可操作性的诊断信息应优先展示data.errors后端报告参数级校验失败时的逐字段映射{ slug: message }键与传给execute的参数 slug 一致若是整体请求失败如外键约束则为空对象{}消息位于data.message中。后端行为可对照 src/metabase/actions/execution.clj 的execute-action!与 src/metabase/actions_rest/api.clj 的执行端点。读取方式无需任何类型断言const message error?.data?.message;渲染错误消息的注意事项SQL 或驱动错误中error.data.message通常包含换行符失败 SQL 语句在下一行。因此必须在支持white-space: pre-wrap的元素中渲染pre即可用span会把换行折叠成一整段文字。{error ? ( pre style{{ whiteSpace: pre-wrap }} {error.data.message ?? Action failed.} /pre ) : null}原样展示错误消息不要替换为通用的 Something went wrong。原始的 SQL / 校验 / 权限错误正是用户修正输入的依据——权限不足时后端返回的正是如 You dont have permissions to do that. 这类可直接展示的文本。源码佐证从 Hook 到后端执行链路useAction的行为可以在仓库中找到端到端证据后端执行端点src/metabase/actions_rest/api.clj 定义POST /api/action/:id/execute先通过eid-translation/-id-or-404解析entity_id拒绝:http类型再调用actions/execute-action!同时支持POST /api/action/:action-id/execute/values预取执行参数值同文件 L192-L204执行分发src/metabase/actions/execution.clj 的execute-action!按类型分发——:implicit走execute-implicit-action!Basic Action:query走execute-custom-action!自定义 SQL Action并处理隐藏参数默认值、缺失参数补齐、多余参数校验check-no-extra-parameters参数键重映射src/metabase/actions_rest/api.clj 的remap-parameter-keys把按 slug 传入的键映射到目标参数:id与文档必须用 slug的要求一致SDK 集成测试sdk-bundle-hooks.cy.spec.tsx 覆盖了useAction的核心行为在MetabaseProvider内/外执行真实 implicitrow/updateAction断言请求体为{ parameters: ACTION_PARAMS }、响应含rows-updated、权限错误经error.data.message呈现result保持 idle、reset()清空result与error。测试还展示了useMetabaseAuthStatus与useAction的配合鉴权成功前不要触发execute否则会收到 401。相关文档与 API 引用Actions 文档Action 的概念、启用条件与权限要求Actions 仅支持 PostgreSQL 与 MySQL且需在数据库连接中开启 Model actionsHook 完整 APIuseAction、UseActionResult类型引用ActionKind、AnyActionResult、ActionResultForKind、ActionResultForCreate、ActionResultForUpdate、ActionResultForDelete、ActionResultForBulk、ActionResultForSql、ActionExecuteError、SdkActionId完整代码示例basic.tsx、parameter-values.tsx、date-picker.tsx、typed-response.tsx、with-refresh.tsx、parallel-refresh.tsx【免费下载链接】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),仅供参考
返回列表