资讯详情

Spree Dashboard Core:以租户作用域替代 StoreProvider,修复卖家面板筛选器崩溃

📅 2026/9/14 22:39:37 | 华诺云谱 👁 阅读
Spree Dashboard Core:以租户作用域替代 StoreProvider,修复卖家面板筛选器崩溃
Spree Dashboard Core以租户作用域替代 StoreProvider修复卖家面板筛选器崩溃【免费下载链接】spreeOpen Source eCommerce Platform for B2B, Marketplace, and Enterprise. REST API, TypeScript SDK, and production-ready Next.js storefront. Self-host it. Own your stack. No vendor lock-in. Zero platform fees.项目地址: https://gitcode.com/GitHub_Trending/sp/spree本篇围绕 Spree 仓库中 changeset table-toolbar-tenant-scoped.md 所记录的修复展开spree/dashboard-core包中的TableToolbar、ResourceCombobox、MediaPickerSheet此前依赖StoreProvider来生成 TanStack Query 的缓存键导致在没有 Store 上下文的卖家seller面板中打开筛选面板时抛出 useStore must be used within a StoreProvider 崩溃。读完后你将理解 Spree Dashboard 的双面板运营者仪表盘 / 卖家面板租户模型、useTenantId的兜底机制以及[resource, tenantId, ...rest]这一规范化缓存键形态的来龙去脉。一、问题背景一个跨面板共享组件踩中的上下文假设changeset 明确说明了缺陷现象与影响范围原文要点症状卖家面板seller panel中打开筛选面板会直接崩溃报错useStore must be used within a StoreProvider根因TableToolbar、ResourceCombobox、MediaPickerSheet三个组件为了限定scope查询键而去调用useStore隐含假设了当前一定挂载了StoreProvider反例场景卖家面板的租户是 seller 而非 store它从不挂载StoreProvider于是共享组件一调用useStore就抛错修复这三个组件改为按租户tenant作用域生成查询键——运营者仪表盘中租户就是 store卖家面板中租户就是 seller附带收益同一个 store 下的两个 seller 之间缓存结果不再可能相互串用此前键只含 store 维度时两个卖家的搜索结果/水合结果会命中同一条缓存。该 changeset 的 frontmatter 标记为spree/dashboard-core: patch说明这是一次补丁级修复不涉及 API 变更。二、崩溃的源头useStore与StoreProvider的契约报错字符串定义在 store-provider.tsxexport function useStore(): StoreContextValue { const context useContext(StoreContext) if (!context) { throw new Error(useStore must be used within a StoreProvider) } return context }StoreProvider本身按storeId拉取并缓存当前 store查询键为[store, storeId]并对外暴露货币、语言、时区等派生数据。同文件中还刻意提供了一个软版本/** * The store context if a StoreProvider is mounted, else null. * * For code that runs in more than one panel. The operators dashboard always * has a store; the seller panel has none — its tenant is a seller, and the * store is derived server-side. ... */ export function useOptionalStore(): StoreContextValue | null { return useContext(StoreContext) }这段注释直接点明了 Spree Dashboard 的面板模型运营者仪表盘一定有一个 store卖家面板没有 store其租户是 sellerstore 由服务端推导。也就是说任何面板都必有 StoreProvider 这条假设在架构层面本来就不成立TableToolbar等共享组件此前用useStore取 id 来拼查询键是典型的把面板特有上下文当成全局上下文的错误。三、修复方案TenantProvideruseTenantId的三级兜底修复引入了租户抽象 tenant-provider.tsx其设计要点全部写在了源码注释中/** * Which tenant the signed-in principal is currently acting as — a store in the * operators dashboard, a seller in the marketplace panel. * * Query keys have to be scoped to it (a cached list must never survive a * switch and be shown under the next tenant), and shared pages cannot reach * for useStore to get it: a seller panel mounts no StoreProvider, so that * throws. useTenantId reads this provider, falls back to the store when only * a StoreProvider is mounted, and answers default when neither is — * which keeps the operators dashboard working with no change at all. */实现非常克制核心只有三行解析逻辑export function TenantProvider({ id, children }: { id: string; children: ReactNode }) { const value useMemo(() id, [id]) return TenantContext.Provider value{value}{children}/TenantContext.Provider } export function useTenantId(): string { const explicit useContext(TenantContext) const store useOptionalStore() return explicit ?? store?.storeId ?? default }可以把它归纳为三级回退显式TenantProvider卖家面板在入口处用TenantProvider id{sellerId}包裹面板根节点见 panel-chrome.tsx此处租户 id 直接是 seller id回退到StoreProvider运营者仪表盘中没有TenantProvider时useOptionalStore()提供storeId行为与修复前等价两者皆无则返回default保证极端情况下查询键仍然稳定、组件不因缺上下文而抛错。这一设计使得运营者仪表盘零改动——不需要给每个面板都挂一个概念上并不存在的 provider共享组件只在有租户上下文时按租户隔离没有时按 store 隔离再没有时用统一的默认段。四、受影响组件的缓存键tenantId进入键的第二段changeset 点名的三个组件其查询键现在都显式携带tenantId1.TableToolbartable-toolbar.tsxTableToolbar是仪表盘所有列表页的筛选/排序/列选择工具条内部多个数据源查询都改为租户作用域ResourceFilterValue把资源筛选 chip 中的 CSV id 列表水合为可读标签时queryKey: [filter-chip, config.queryKey, tenantId, ids]源码注释解释了动机——Tenant-scope the cache key so chips dont resolve against another tenants hydrate results after a store or seller switch切换 store 或 seller 后chip 不能再拿另一个租户的水合结果来解析。QuickResourceFilter可listAll的资源列如仓库、供应商的快速筛选queryKey: [quick-filter-all, config.queryKey, tenantId]筛选面板内部的标签与资源选择查询同样带上tenantId如[panel-tags, tenantId, column.taggableType]、[panel-resource-filter, ..., tenantId, deferredQuery]并且面板在切换字段/租户时会比较previousTenant tenantId来决定是否清空上一轮结果避免跨租户的残留显示。2.ResourceComboboxresource-combobox.tsx资源下拉框的两个数据源——搜索与水合——都改为queryKey: [queryKey, tenantId, search, trimmedQuery] // L106 queryKey: [queryKey, tenantId, hydrate, value] // L1163.MediaPickerSheetmedia-picker-sheet.tsx媒体选择弹窗的搜索查询键为queryKey: [queryKey, media-picker, tenantId, trimmedQuery] // L94值得注意的是这一处修复解决的不仅是崩溃还消除了一个更隐蔽的数据正确性隐患此前若键只含 store 维度同一 store 下的两个 seller 在卖家面板切换时后进入的 seller 可能直接命中先前的 seller 的搜索/水合缓存——从源码结构看这正是 changeset 中 cached results can no longer be shared between two sellers of the same store either 一句对应的实现层面。五、配套规范useResourceKey与[resource, tenantId, ...rest]键形态该修复并非孤立改动而是落在 query-keys.ts 定义的规范化键形态上/** * Canonical TanStack query-key shape for every dashboard resource: * * [resource, tenantId, ...rest] * * Use useResourceKey (the hook) from inside React components to build * tenant-scoped keys — it reads the id from TenantProvider (falling back * to StoreProvider) so callers never spell it themselves */ export function useResourceKey(resource: string, ...rest: ReadonlyArrayunknown): QueryKey { const tenantId useTenantId() return [resource, tenantId, ...rest] }由此形成一套完整的键工具链API用途resourceKey(resource, ...rest)不读租户上下文的裸构建器留给测试、构建期默认值等非 hook 场景useResourceKey(resource, ...rest)组件内默认选择自动注入当前租户 iduseResourceKeyBuilder()返回稳定闭包适用于 id 只有运行时才知道的场景如 mutation 的onSuccess中按variables.id删键withStoreScope(key, storeId)在键的第 1 段幂等地注入 storeId已有则原样返回供列表类组件统一前缀列表页主表 resource-table.tsx 即通过const queryKeyPrefix withStoreScope(userPrefix, tenantId)L345、L354 一带把租户段合入用户提供的键前缀与上述三个组件的做法保持一致。六、修复的架构含义与验证路径从源码结构可以归纳出这次 patch 的三条设计结论共享组件不得假设面板级上下文存在。useStore是硬依赖无则抛错useOptionalStore是软依赖无则返回null跨面板组件应使用软依赖或更上层的租户抽象租户是比 store 更通用的缓存隔离单位。运营者仪表盘里 tenant store卖家面板里 tenant seller用useTenantId的三级回退可以让同一份dashboard-core代码在两种面板中复用且互不污染缓存缓存键的租户段位于第 1 段之后[resource, tenantId, ...rest]这一位置约定由useResourceKey、withStoreScope共同维持新写仪表盘数据查询时应优先复用这两个工具而非手拼键。如需在仓库中复核本修复可按以下路径阅读缺陷描述 .changeset/table-toolbar-tenant-scoped.md → 崩溃抛出点 store-provider.tsx → 租户抽象 tenant-provider.tsx → 键规范 query-keys.ts → 调用方 table-toolbar.tsx、resource-combobox.tsx、media-picker-sheet.tsx以及卖家面板入口 panel-chrome.tsx 中TenantProvider id{sellerId}的挂载方式。【免费下载链接】spreeOpen Source eCommerce Platform for B2B, Marketplace, and Enterprise. REST API, TypeScript SDK, and production-ready Next.js storefront. Self-host it. Own your stack. No vendor lock-in. Zero platform fees.项目地址: https://gitcode.com/GitHub_Trending/sp/spree创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。