@nuskin/react-product-sdk
v1.2.0
Published
React bindings and connected components for Nu Skin product-domain capabilities.
Readme
@nuskin/react-product-sdk
React bindings and connected components that connect approved product-domain capabilities to presentational React components.
This package is the React-facing integration layer between consuming
applications, @nuskin/product-lib, and @nuskin/react-product-components. It
owns React providers, hooks, connected-component orchestration, and
domain-to-view mapping. It does not own product GraphQL transport or reusable
visual design.
Business rules remain in domain SDKs. Application composition remains in the consuming MFE or container. This package coordinates those capabilities without collapsing the boundaries between them.
Architecture contract
MFE / consuming application
|
| supplies session, configuration, routing, and SDK clients
v
@nuskin/react-product-sdk
|
+--> @nuskin/product-lib --> product-domain services
|
+--> @nuskin/react-product-components --> foundation UI and themeDependencies must flow downward. Reverse dependencies are forbidden.
Non-negotiable boundaries
- Experience components must consume product data through an approved product domain SDK rather than executing product GraphQL directly.
- Experience-owned HTTP integrations are allowed when no domain SDK owns the capability. They must not duplicate product SDK responsibilities.
- Domain models should be converted into explicit UI props through separately testable mapper functions.
- Runtime session, configuration, routing, and domain clients are supplied by the consuming application or an approved integration mechanism.
- Reusable visual design belongs in
@nuskin/react-product-components. - Product components must remain usable with static props and callbacks. They must not access the network or depend on this package, a domain SDK, GraphQL, or application session state.
- Local visual state may remain in the component library. Product, customer, cart, pricing, inventory, configuration, and session source-of-truth state may not.
See the architecture documentation for detailed ownership rules and CONTRIBUTING.md for the review checklist.
Current status
The initial connected product-card and product-carousel slices are implemented.
They provide a typed client boundary over @nuskin/product-lib, client
injection through ProductProvider, the useProductById async-state hook,
ProductCard, and a windowed ProductCarousel.
@nuskin/product-lib is the initial product-data dependency.
ProductCard currently renders product content and images only. It does not
map pricing or inventory, and it does not provide an add-to-cart callback, so the
presentational component hides its CTA. Product clicks open the canonical PDP
URL supplied by the product domain response in the same browser tab. Missing,
malformed, and non-HTTP(S) canonical URLs do not trigger navigation.
ProductCarousel accepts the complete ordered product-ID collection. It
reserves every position, preserves duplicates, loads the first six positions
immediately, and uses the presentational carousel's internal demand signal to
maintain one logical page of look-ahead. Product-data service requests contain
at most five distinct IDs. Null, missing, not-found, failed, and invalid-PDP
positions remain in place as error slots.
The demand window controls product-data requests. Separately, the presentational package mounts full ProductCard trees only near the visible carousel viewport while retaining lightweight slots elsewhere. Neither mechanism is a reusable product cache.
Usage
import {
createProductClient,
ProductCard,
ProductCarousel,
ProductProvider,
} from '@nuskin/react-product-sdk';
const productClient = createProductClient({
environment: 'test',
market: 'US',
language: 'en',
});
export function Example() {
return (
<ProductProvider client={productClient}>
<ProductCard productId="bltddf3c9598da66cbe" />
<ProductCarousel
ariaLabel="Featured products"
productIds={[
'bltddf3c9598da66cbe',
'blt67368080ff19e808',
'01001647',
]}
/>
</ProductProvider>
);
}Applications with authenticated product requests can supply
getAccessToken. It is evaluated for each request so the client does not retain
a stale session token:
const productClient = createProductClient({
environment: runtime.environment,
market: runtime.market,
language: runtime.language,
getAccessToken: () => session.getAccessToken(),
});Provider lifecycle
Place ProductProvider above the product components and hooks that share the
same environment, market, and language. It will commonly sit immediately below
the consuming application's session, configuration, and locale providers.
The client captures environment, market, language, and
useContentSource when it is created. Recreate it when one of those values
changes. Passing a new client to ProductProvider causes active product hooks to
request data using the new configuration:
import { useMemo } from 'react';
const productClient = useMemo(
() =>
createProductClient({
environment: runtime.environment,
market: locale.market,
language: locale.language,
useContentSource: runtime.contentSource,
getAccessToken: () => session.getAccessToken(),
}),
[
runtime.environment,
runtime.contentSource,
locale.market,
locale.language,
],
);
return <ProductProvider client={productClient}>{children}</ProductProvider>;Do not call createProductClient unconditionally during each render. A new
client changes the context value and causes product hooks to request their data
again. Session-token changes alone do not require a new client:
getAccessToken is evaluated for every request.
ProductProvider shares the client instance, not request state or product
results. Standalone cards requesting the same product issue independent product
requests. A connected carousel deduplicates IDs within each of its own batches,
but it is not a reusable product cache and does not deduplicate work with other
cards or carousel instances.
Consumers that need custom rendering can use useProductById inside the
provider and receive product, isLoading, and error directly.
Scripts
| Command | Purpose |
| -------------------- | ------------------------------------------------- |
| yarn build:package | Build CommonJS, ESM, and TypeScript declarations |
| yarn lint | Check source architecture and code-quality rules |
| yarn types:check | Type-check without emitting files |
| yarn test | Run Jest and collect coverage |
| yarn validate | Run formatting, lint, types, tests, and the build |
Storybook, a development host, browser automation, and GraphQL tooling are intentionally omitted. They should be introduced only when a concrete problem requires them.
License
MIT
