ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Langfuse Tracing Tables 前端状态架构:Per-Mount Local Store 模式与行选择状态管理解析

Langfuse Tracing Tables 前端状态架构:Per-Mount Local Store 模式与行选择状态管理解析 Langfuse Tracing Tables 前端状态架构Per-Mount Local Store 模式与行选择状态管理解析【免费下载链接】langfuse Open source AI engineering platform: LLM evals, observability, metrics, prompt management, playground, datasets. Integrates with OpenTelemetry, LangChain, OpenAI SDK, LiteLLM, and more. YC W23项目地址: https://gitcode.com/GitHub_Trending/la/langfuse本指南以 Langfuse 仓库中web/src/features/tracing-tables目录为核心深入讲解其采用的per-mount local state按挂载实例的本地状态架构如何用 vanilla Zustand 创建独立于组件树的表格行选择 Store、如何通过 Context 与selectionStoreprop 将状态边界清晰隔离以及如何借助测试用例锁定核心行为。读完本文你将掌握 Langfuse 前端大功能tracing table 类表面中状态归属、订阅切片、生命周期桥接的一套可复用的落地范式并能在observations观测表中定位到每一处状态的归属与实现。背景为什么 tracing table 需要 Local StoreLangfuse 的前端页面观测、Trace、评分等追踪类表格是典型的本地状态重负载场景筛选器、已保存视图、选中行、展开行、抽屉peek、懒加载行状态、视图级操作等通常只属于一个已挂载的页面实例而不是整个应用。若让一个组件或 Hook 独揽全部状态一次复选框点击、悬停或行选择变化就会连带重跑构建筛选、列定义、数据包装、昂贵单元格、抽屉与路由胶水的代码。仓库中.agents/skills/frontend-large-feature-architecture/references/local-feature-state.md明确给出了该模式的定义当状态是高频变更、需要在挂载特性的多个子树间共享、或必须跨越行级重挂载存活时才引入一个本地的 vanilla Zustand store并遵循以下默认形态页面/视图通过惰性useState创建一个 Store 实例Context 只提供这个稳定的 Store 实例组件订阅最小的有用切片变更逻辑收敛到具名 Store actions复杂用户工作流放actions/*.ts或 Store actions昂贵单元格/行保持只读收窄容器包裹大型特性根目录维护一份简短的README.mdOwner Map即本文主角所做的事情。与之配套的还有明确的反模式清单共享的src/components/*组件不得调用特性级 Store Hook会静默破坏未挂载 Provider 的其他调用方不得用useMemo持有外部 Store 实例不得把仅属于单个挂载页面的状态提升为全局 StoreContext Provider 的value不得随行选择、悬停、滚动等高频状态变化。tracing-tables目录正是这套原则在观测表格上的具体落地。目录结构与 Owner Map三个文件各司其职web/src/features/tracing-tables的目录结构如下含observations子目录web/src/features/tracing-tables/ ├── README.md └── observations/ ├── ObservationsTableStoreProvider.tsx # 特性级 Context Provider ├── observationsTableStore.clienttest.ts # 纯逻辑的 vitest 覆盖 ├── observationsTableStore.ts # vanilla zustand Store 定义与工厂 └── useObservationsTableView.ts # 每挂载创建 Store 并桥接外部状态原文档定义的 Owner Map 可归纳为下表文件职责覆盖/配套observationsTableStore.ts拥有行选择row selection、全选select-all、当前页选中行推导selected-page-row derivation以及加入数据集对话框标志observationsTableStore.clienttest.ts纯逻辑测试useObservationsTableView.ts每次挂载lazyuseState创建 Store 实例并将 URL/会话存储中的useSelectAll状态桥接进 Store—ObservationsTableStoreProvider.tsx特性级 Context供工具栏、对话框等特性组件消费共享组件绝不消费该 Context—关键约束在于第三点共享组件DataTable与TableSelectionManager通过selectionStoreprop显式接收Store见web/src/components/table/table-selection-store.ts因此嵌套表格例如观测 peek 内的 ScoresTable不会受外层选择状态干扰。核心实现vanilla Zustand 的 Store 工厂web/src/features/tracing-tables/observations/observationsTableStore.ts使用zustand/vanilla的createStore构建 Store并扩展共享的TableSelectionStoreState接口export interface ObservationsTableStoreState extends TableSelectionStoreState { showAddToDatasetDialog: boolean; actions: TableSelectionStoreState[actions] { syncPageRows: (payload: { pageRowIds: string[]; totalCount: number | null }) void; syncSelectAll: (selectAll: boolean) void; setShowAddToDatasetDialog: (isOpen: boolean) void; }; }Store 的初始状态包含rowSelectionRowSelectionState记录tanstack/react-table类型、selectAll、selectedPageRowIds、pageRowIds、totalCount、showAddToDatasetDialog。创建时接受两个外部参数形成一条双向桥接契约export function createObservationsTableStore({ initialSelectAll, onSelectAllChange, }: { initialSelectAll: boolean; onSelectAllChange: (selectAll: boolean) void; }): ObservationsTableStoreinitialSelectAllStore 首次创建时从 URL/会话存储读入的全选初始值onSelectAllChangeStore 内部全选状态变化时的回写回调用于把状态同步回useSelectAll的会话存储层。内部的关键派生逻辑Store 内部有一个核心辅助函数updateSelection它在任何选择变更时同步重算selectedPageRowIds——即当前页内被勾选的行 id 列表由pageRowIds过滤rowSelection得到function getSelectedPageRowIds(rowSelection: RowSelectionState, pageRowIds: string[]) { return pageRowIds.filter((rowId) Boolean(rowSelection[rowId])); }setSelectAll在真正变化时才写状态并通知桥接回调防止无意义的会话存储写入toggleRows支持批量勾选/取消取消勾选时强制把selectAll置为false并通知外部togglePageRows的语义尤为明确——勾选本页全部行时保留其他页已选行取消本页全选时则清空整个选择。syncPageRows与syncSelectAll是供外部 → Store方向使用的同步入口前者在分页数据到达时更新当前页行 id 列表并重算选中行即便同一行在翻页后仍被选中其选中状态也会因rowSelection保留而正确恢复见测试用例后者用于路由变化等场景把外部selectAll复位同步进 Store且不会回写外部避免回声。生命周期桥接useObservationsTableViewweb/src/features/tracing-tables/observations/useObservationsTableView.ts是每挂载一个 Store的创建点遵循 local-feature-state 规范中惰性useState持有外部 Store 实例的写法export function useObservationsTableView({ projectId }: { projectId: string }) { const { selectAll, setSelectAll } useSelectAll(projectId, observations); const [store] useState(() createObservationsTableStore({ initialSelectAll: selectAll, onSelectAllChange: setSelectAll }), ); // ... return { store }; }URL/会话存储层的useSelectAll桥接web/src/features/table/hooks/useSelectAll.ts实现了外部状态层以selectAll-${projectId}-${tableName}为 key 读写sessionStoragetableName 此处为observations并使用 Next.jsrouter.events监听routeChangeStart——路由开始变更时立即把全选复位为false保证跨页面导航不会残留全选标记。为什么用useLayoutEffect做同步桥接 Hook 中有一个值得注意的细节useLayoutEffect(() { store.getState().actions.syncSelectAll(selectAll); }, [selectAll, store]);选择useLayoutEffect而非useEffect是为了让 selectAll 的复位例如路由变化触发的setSelectAll(false)在浏览器绘制paint之前到达已订阅的行组件避免出现一帧过期高亮的闪烁。Store 创建点的使用方式在web/src/components/table/use-cases/observations.tsx的控制器组件中useObservationsTableView在顶部被调用一次得到的 Store 实例随后被两处使用显式传给共享表格组件——DataTable ... selectionStore{observationsTableStore} /见 observations.tsx包进特性级 Provider——ObservationsTableStoreProvider store{observationsTableStore}包裹内容树见 observations.tsx。Context 提供与消费特性组件与共享组件的隔离web/src/features/tracing-tables/observations/ObservationsTableStoreProvider.tsx提供特性作用域的 Context。它的useObservationsTableStore是一个带 selector 的订阅 Hookexport function useObservationsTableStoreTValue( selector: (state: ObservationsTableStoreState) TValue, ) { const store useContext(ObservationsTableStoreContext); if (!store) { throw new Error(useObservationsTableStore must be used within ObservationsTableStoreProvider); } return useStore(store, selector); }在observations.tsx的工具栏组件ObservationsDataTableToolbar中可以看到最小切片的订阅实践——各自只订阅自己需要的状态const selectedObservationIds useObservationsTableStore((state) state.selectedPageRowIds); const selectAll useObservationsTableStore((state) state.selectAll); const actions useObservationsTableStore((state) state.actions); const selectedObservationCount selectAll ? totalCount : selectedObservationIds.length;DataTable侧的 context-free 选择 API共享组件端由web/src/components/table/table-selection-store.ts提供一套context-free的最小接口。它定义了TableSelectionStoreLike仅需subscribe与getState并通过useSyncExternalStore提供三个渲染钩子useTableRowIsSelected、useTableSelectAll、useTableRowSelection。未传入 Store 时使用subscribeNoop与 fallback 值因此不依赖任何 Provider 的调用方依然可以渲染——这正是嵌套表格如 peek 内的 ScoresTable不受外层选择状态影响的机制保证。DataTable内部据此按行订阅、按需重渲染避免了一次勾选重渲染整表的性能陷阱。共享选择基座createTableSelectionStore值得顺带指出table-selection-store.ts中还有一个不依赖外部状态的通用实现createTableSelectionStore它只做rowSelection/selectAll的纯本地管理无syncPageRows、无showAddToDatasetDialog。观测表 Store 是在它的基础上扩展了分页同步与对话框标志——两套实现共享同一组 action 命名setRowSelection、setSelectAll、toggleRow(s)、togglePageRows、clearSelection保证了TableSelectionManager等共享组件可用同一套 API 驱动不同来源的 Store。状态边界划分谁拥有什么原文档将观测表表面的状态划分为三条清晰的边界这也是整套架构的精髓状态类别归属选择状态selection state本目录的 Storetracing-tables服务端/查询状态server/query state表格 use-case 组件中的tRPC路由/筛选状态route/filter state表格 use-case 组件中的URL hooks已冻结见下节也就是说Store 不感知 tRPC 的加载态、错误态与数据获取use-case 组件不亲自维护rowSelection的派生计算筛选与路由参数始终留在 URL hooks 层与 Store 完全解耦。这种每类状态只在一处的划分使每个变更都能唤醒语义上依赖它的那一小部分 UI。测试保障纯逻辑的确定性验证Store 是纯 vanilla Zustand因此可以在 Node 环境用 vitest 直接验证无需渲染 React 树。web/src/features/tracing-tables/observations/observationsTableStore.clienttest.ts标注// vitest-environment node通过createTestStore工厂注入vi.fn()的onSelectAllChange覆盖了以下关键行为行级切换toggleRow(a, true)后rowSelection与selectedPageRowIds同步更新取消勾选联动取消勾选会同时把selectAll置false并调用onSelectAllChange(false)批量切换toggleRows([b,c], true)保留已有选择、只做并集整页选择语义togglePageRows勾选时保留其他页已选行取消时清空全部选择函数式 updatersetRowSelection((previous) ...)正确解析tanstack/react-table的Updater形态桥接防回声setSelectAll只在真正变化时通知外部连续两次setSelectAll(true)只回调一次syncSelectAll更新状态但不触发回调翻页重算syncPageRows换页后selectedPageRowIds按新页行 id 重算翻回原页时已选行正确恢复清空clearSelection重置选择与全选并通知外部。这套测试把最容易出错的页级选择 × 全选 × 跨页保留语义锁死为可回归的契约是 local-store 模式纯逻辑可测优势的直接体现。现状与演进冻结的旧页面与新表面原文档的 Status 部分如实交代了该目录当前所处的演进阶段ObservationsTableweb/src/components/table/use-cases/observations.tsx约 1600 行目前仍是控制器组件——内联构建列、从多个 hooks 组装查询状态、在渲染期准备数据。按 2026-06 的决策遗留的 observations 页面被冻结bugfix-only筛选/搜索这条纵向能力改在特性开关flag后重建而非继续向该控制器迁移。但本目录产出的两件资产已被重建后的新表面复用选择 StoreobservationsTableStore.ts及其工厂无 Context 依赖的DataTable选择 APItable-selection-store.ts的selectionStoreprop 与三个渲染钩子。对后续维护者而言这意味着在冻结页面上只做 bug 修复新表面直接复用本目录的 Store 与选择 API并按 local-feature-state 的迁移步骤先埋点定位宽渲染 → 提取最小边界 → 窄边界订阅 → 具名 actions → 稳定回调 → 纯函数化数据准备 → 更新 README逐步消化剩余的状态分散。模式提炼如何在你的特性中复用这套架构从tracing-tables目录可以提炼出一份可移植的 checklist判定是否用 Store状态是否高频变更、是否需跨子树共享、是否需跨行重挂载存活若只是内联导出流程或列构建器先提取纯函数不要为了显得有架构而加 Store创建在视图层用惰性useState(() createXxxStore(...))创建一个已提交挂载视图对应一个 Store 实例不要用useMemo持有桥接把 URL/会话存储等外部状态通过initialXxx/onXxxChange注入双向同步需要外部→Store单向同步时提供sync*动作并避免回声提供Context 只放稳定 Store 实例特性组件用带 selector 的 Hook 订阅最小切片共享隔离共享组件只接受selectionStore之类的 prop或TableSelectionStoreLike最小接口绝不偷偷消费特性 Context测试Store 保持纯 vanilla 逻辑用 vitest 覆盖页级选择、全选联动、跨页保留、桥接防回声等边界语义文档特性根目录维护简短 README Owner Map记录谁拥有什么、当前分散了什么、下一步迁移哪一块而非当变更日志使用。上述每一环都能在web/src/features/tracing-tables、web/src/components/table/table-selection-store.ts、web/src/features/table/hooks/useSelectAll.ts及.agents/skills/frontend-large-feature-architecture/references/local-feature-state.md中找到一一对应的落地证据——它既是 Langfuse 观测表格当前的状态架构也是仓库内大型前端特性推荐的演进范式。【免费下载链接】langfuse Open source AI engineering platform: LLM evals, observability, metrics, prompt management, playground, datasets. Integrates with OpenTelemetry, LangChain, OpenAI SDK, LiteLLM, and more. YC W23项目地址: https://gitcode.com/GitHub_Trending/la/langfuse创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表