本文档介绍如何在 TanStack 生态中协调 Charts 与各数据、状态及交互库,围绕完整的产品状态图执行 trigger → inspect → decide → build → verify 的流程。在连接各库之前,需要为每个行投影、语义键、筛选、选择、视口、修订、事务和时序策略指定唯一负责人。
设置:检查归属图
在编写适配器之前记录以下边界(关注点 / 正常归属 / 图表输入):
- 远程获取、缓存、新鲜度、重试:归属 Query,图表输入为已解析的语义快照加状态。
- 规范化记录、实时查询、乐观事务:归属 DB 或同步集合,图表输入为带键投影加同步状态。
- 网格筛选、排序、分组、分页:网格本地时归属 Table,共享时归属应用,图表输入为显式选择的行模型边界。
- 视觉编码、焦点、场景、渲染器:归属 Charts,图表输入为已准备的行和已提交的行为选项。
- 共享客户端意图:归属 Store 或应用状态,图表输入为语义筛选、键和域。
- 可恢复的导航状态:归属 Router search,图表输入为经过验证的紧凑意图,而非像素或行对象。
- 已挂载的网格窗口:归属 Virtual,除非“可见行”是明确的问题,否则不影响分析。
- 突发、队列或批处理策略:归属 Pacer,图表输入为有节奏的源修订或已提交操作。
- 草稿控件与验证:归属 Form 或本地状态,图表输入为已提交且有效的意图。
- SSR 请求与水合:归属 Start 或框架生命周期,图表输入为确定性的数据修订、区域设置和初始几何。
失败条件:两个库可以独立更改同一语义状态,或者图表接收的行没有具名范围和修订。
命名每个行投影
不要在界面之间传递“那些行”,而要命名分析边界:
- sourceRows:此客户端可用的规范记录。
- eligibleRows:经过共享业务过滤后的记录。
- summaryRows:聚合或分箱后的图表粒度。
- detailRows:仅展示性排序或分页前的网格粒度。
- visibleRows:当前挂载的虚拟项;通常仅用于展示。
- selectedRows:由稳定语义键解析出的行。
记录网格排序、筛选、分组、分页或选择是否会改变图表问题。行模型选择请参考 Table 与 data-grid 参考。
核心模式
派生一次,有意识地投影
使用一个语义投影负责人,然后分别暴露 summary 和 detail 输出:
const analysis = projectOrders(sourceRows, sharedIntent)
const tableData = analysis.detailRows
const chartDefinition = defineChart({
marks: [
lineY(analysis.summaryRows, {
x: 'day',
y: 'revenue',
color: 'region',
key: ({ datum }) => `${datum.region}:${datum.day.toISOString()}`,
}),
],
scales: {
x: { scale: scaleUtc },
y: { scale: scaleLinear },
},
})
不要在 Table 访问器和图表变换中独立重述资格、聚合或指标公式。仅展示性的表格排序和分页可以保留在网格内部。
同步语义意图,而非组件内部
共享以下值:
- 实体 ID 或聚合桶键;
- 经过验证的筛选器和比较周期;
- 已提交的比例域或时间窗口;
- 选定的指标和分组维度;
- 数据修订和“截至”状态。
将 DOM 节点、行索引、图表像素、虚拟索引、工具提示内容和 Table 行对象保留在各自负责的界面内部。
区分瞬时、已提交和持久化状态
在交互可以写入数据时使用三个阶段:
- 瞬时:悬停、指针位置、拖动预览和活动手柄保持本地且廉价。
- 已提交:选择、接受的刷选域和提交的筛选器一次性更新共享应用或 URL 状态。
- 持久化:DB 或服务器写入显式暴露 pending、confirmed、failed 和 rollback 状态。
不要将每个指针帧写入 Router、Query、同步引擎或数据库事务。当记录是实时的或可写时,读取 Query、DB 和 sync。
在昂贵边界处控制节奏
按语义选择时序:
- 对应等待意图的草稿搜索或筛选器使用 debounce;
- 对需要有限频率的一次性视觉预览使用 throttle;
- 当每个操作都必须持久化时使用队列写入;
- 批处理兼容的变更、遥测或源修订;
- 即使写入无损,也要为图表展示保持 latest-wins 正确性。
不要在图表宿主、实时查询引擎或框架已经限定的工作周围添加第二个调度器。
验证完整的同步序列
- 将具名数据修订加载到两个界面。
- 应用共享筛选器,证明汇总总计与明细行对账。
- 对网格排序和分页;证明图表仅在预期时变化。
- 从图表和网格中选择;证明两者解析出相同的语义键。
- 应用乐观更新;区分 pending 与 confirmed 输出。
- 回滚或接收远程更新;保留仍存在的选择并清除缺失的键。
- 恢复 URL 状态、水合、调整大小、虚拟化并销毁,不产生重复订阅或写入。
常见错误
- CRITICAL 意外地对最终渲染行模型绘图:错误做法是无论网格当前渲染什么行都喂给图表;正确做法是根据所述比较选择过滤、分组、分页前、页面或选定的边界。Table 行模型可能包含排序、分组、展开和分页;最终网格行模型是展示结果,不自动是有效的分析数据集。来源:TanStack Table 行模型指南。
- CRITICAL 按行或虚拟索引进行协同:错误做法是同步 row.index、数组位置或虚拟项索引;正确做法是定义应用级实体或聚合键,并让 Table 和 Charts 都适配它。索引在排序、筛选、分页、流式传输和虚拟化下会变化。分组 Table 行 ID 也可能包含展示后缀。来源:TanStack Table 行指南;API-FRICTION.md F-120、F-131 和 F-239。
- CRITICAL 重复过滤和聚合:错误做法是在 Table 聚合函数中实现 KPI,又在图表准备中再次实现;正确做法是派生一个具名语义投影,可供给网格、图表、表格替代品、导出和测试。独立管道会在 null 策略、分母、时间边界和后续产品变更上产生漂移。来源:docs/guides/transforms-and-reactivity.md;API-FRICTION.md F-128 和 F-163。
- CRITICAL 创建双向状态循环:错误做法是图表选择设置 Table 筛选器,其变化又重建图表选择并再次写入 URL;正确做法是指定一个负责人,将一个事件规范化为语义意图,并让每个界面从该状态派生而不回显等价更新。反馈循环会导致冗余渲染、历史垃圾、闪烁和无法稳定的状态。来源:Charts 交互归属;TanStack Router search 状态指南。
- HIGH 将乐观行视为已确认历史:错误做法是将乐观变更动画加入普通历史序列而不带 pending 状态;正确做法是将 sync 或事务状态带入语义投影,在决策相关时区分 pending 值,并验证回滚。TanStack DB 在持久化前应用乐观状态,可在同步后替换或回滚。来源:TanStack DB mutations 和 live-query 指南。
- CRITICAL 持久化每一帧交互:错误做法是通过 Router、Query 失效或同步事务写入每个刷选、手柄、调整大小或指针更新;正确做法是本地预览,必要时对昂贵派生控速,并在交互边界提交一个语义值。帧率状态是展示;持久化需要验证、取消、排序和冲突语义。来源:Charts 交互指南;TanStack Pacer 时序指南。
- HIGH 将虚拟化视为分析过滤:错误做法是仅对 Virtual 当前暴露的已挂载网格行绘图;正确做法是对预期的源或过滤投影绘图;仅在明确视口范围问题中使用挂载行。Virtual 控制渲染工作,而非数据集含义。来源:TanStack Virtual virtualizer 参考。
参考
- Table 和数据网格
- Query、DB 和同步引擎
- TanStack 应用状态和生命周期
另见:prepare-chart-data/SKILL.md、build-chart-interactions/SKILL.md、update-and-animate-charts/SKILL.md 和 debug-and-verify-charts/SKILL.md。