Skip to content

✨ feat(prices): standalone Price with module-level batch store, deprecate PricesContainer#752

Merged
acasazza merged 8 commits intov5.0.0from
feat/751-use-prices-container-replace-reducer
Apr 23, 2026
Merged

✨ feat(prices): standalone Price with module-level batch store, deprecate PricesContainer#752
acasazza merged 8 commits intov5.0.0from
feat/751-use-prices-container-replace-reducer

Conversation

@acasazza
Copy link
Copy Markdown
Member

@acasazza acasazza commented Apr 22, 2026

Summary

Closes #751
Closes #748

Implements standalone <Price> component that works without PricesContainer, using a module-level debounce batch store for automatic request batching.

Changes

packages/hooks

  • pricesBatchStore.ts — new module-level Map keyed by accessToken; 50 ms debounce collects all SKU registrations into a single usePrices call
  • usePrices.ts — subscribe to batch store via useSyncExternalStore; SWR deduplication ensures a single HTTP request per unique set of SKU codes + token
  • 100% test coverage (69 tests)

packages/react-components

  • Price.tsx — detect standalone mode (setSkuCodes == null), register/unregister SKU via store, return ReactNode, show loader while fetching
  • price.spec.tsx — mock @commercelayer/hooks to avoid React 19 act() deadlock; 5 new tests, 100% branch coverage

packages/document

  • prices.stories.tsx — new StandalonePrice story (3 <Price> without container)
  • 001.prices.mdx — standalone pattern promoted to top; PricesContainer marked deprecated with migration hints
  • 005.containers.mdx — deprecation notice added; before/after code examples; typos fixed

Deprecation

PricesContainer is deprecated and will be removed in a future major release.

Before (deprecated):

<PricesContainer skuCode="MY-SKU">
  <Price />
</PricesContainer>

After (recommended):

<Price skuCode="SKU-A" />
<Price skuCode="SKU-B" />
<Price skuCode="SKU-C" />

All three <Price> instances are batched into a single API request automatically via the 50 ms debounce store.

- Delete utils/getSdk.ts — no longer needed
- Replace all local getSdk imports with getSdk from @commercelayer/core
- Remove endpoint from CommerceLayerContext usage across all components/reducers
- Derive org slug/domain from JWT instead of endpoint string
- Fix Price.tsx TS return type (wrap loader/pricesComponent in fragments)
- Update @commercelayer/sdk to 7.9.0 to align with @commercelayer/core peer dep
- Fix Storybook prices: stale hooks/core dist was root cause of missing prices fetch
- Fix HostedCart: move jwt() decode inside async handlers to avoid invalid token error
- Update hosted-cart.spec.tsx to use valid JWT structure
Will be removed in a future major release.
Replacement: use <Price skuCode="MY-SKU" /> standalone,
or usePrices hook for batched multi-SKU requests.
Replace PricesContainer-required pattern with a provider-free batching
architecture so <Price> works standalone and auto-batches SKU requests.

- Add pricesBatchStore.ts: module-level Map keyed by accessToken; collects
  SKU codes via registerSku/unregisterSku with 50 ms debounce; notifies
  useSyncExternalStore listeners on each flush; cleans up store entry when
  last subscriber unmounts
- Rewrite usePrices: subscribe to batch store via useSyncExternalStore;
  auto-call fetchPrices when snapshot changes; expose registerSku/unregisterSku
  wrappers; SWR deduplicates identical param calls to one HTTP request
- Add 'DOM' to hooks/tsconfig.json lib (required for setTimeout types)
- Update Price.tsx: detect standalone mode (setSkuCodes == null); call
  usePrices directly with real token; filter batchPrices by sku_code
- Add pricesBatchStore.test.ts (11 tests, 100 % coverage)
- Add/extend usePrices.test.ts (20 tests, 100 % coverage)
- Rewrite price.spec.tsx: mock @commercelayer/hooks to avoid React 19
  act() deadlock from useSyncExternalStore + SWR; 5 tests cover all
  branches including filter callback and accessToken ?? '' fallback
Add StandalonePrice story showing 3 <Price> components used directly
without a PricesContainer parent. All three auto-batch into a single
API request via the module-level debounce store in usePrices.
…nder

{ loader } and { pricesComponent } were JS object expressions, not JSX.
React 19 correctly throws when an object is returned as a child.
Replace with <>{loader}</> and <>{pricesComponent}</> fragments.
…agments

- Change Price return type JSX.Element → ReactNode (React 19 idiomatic)
- Return loader and pricesComponent directly without <></> wrappers
- Fix price.spec.tsx: use ReturnType<typeof usePrices> instead of
  unexported UsePricesReturn interface
…esContainer

- Add StandalonePrice story showing 3 <Price> components without container
- Update 001.prices.mdx: standalone pattern first, deprecation notices
- Add loader prop to props table
- Add migration hints from PricesContainer to standalone <Price>
…omponents

- Add deprecation notice for PricesContainer and container pattern in general
- Show before/after code examples (old container vs new standalone)
- Fix typos: 'To amultiple', 'documentend'
- Keep Hierarchy section intact
@acasazza acasazza added enhancement New feature or request task components Components package labels Apr 22, 2026
@acasazza acasazza self-assigned this Apr 22, 2026
@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 22, 2026

Deploy Preview for commercelayer-react-components failed.

Name Link
🔨 Latest commit 8ea3abc
🔍 Latest deploy log https://app.netlify.com/projects/commercelayer-react-components/deploys/69e8f7b4af21b5000857f841

@acasazza acasazza changed the base branch from main to v5.0.0 April 23, 2026 08:16
@acasazza acasazza merged commit 8947a0d into v5.0.0 Apr 23, 2026
2 of 6 checks passed
@acasazza acasazza deleted the feat/751-use-prices-container-replace-reducer branch April 23, 2026 08:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

components Components package enhancement New feature or request task

Projects

None yet

1 participant