Skip to main content

sfnext-state-management

Choose the right state mechanism in a Storefront Next project: loader and action data, useRouteLoaderData, URL state with useSearchParams, cookies and sessions, optimistic UI (fetcher.formData, useNavigation, useOptimistic), React context providers (BasketProvider, AuthProvider, wishlist, product-context), and small useSyncExternalStore stores such as the mini-cart store. Use for "where should this state live", "useBasket", "useAuth", "share state between components", "optimistic add to cart", "global client state", "hydration mismatch from a store", or "do I need Zustand". Do not use for fetching or mutating data (use `storefront-next:sfnext-data-fetching`), controlling loader re-runs (use `storefront-next:sfnext-revalidation`), or login flows and sessions (use `storefront-next:sfnext-authentication`).

Quellinformationen

Repository
SalesforceCommerceCloud/b2c-developer-tooling
Letzte Quellaktivität
4. Oktober 2026 um 16:40
Erkannte Sprache von SKILL.md
Englisch
Sterne
56
Forks
22

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
3 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
sfnext-state-management
description
Choose the right state mechanism in a Storefront Next project: loader and action data, useRouteLoaderData, URL state with useSearchParams, cookies and sessions, optimistic UI (fetcher.formData, useNavigation, useOptimistic), React context providers (BasketProvider, AuthProvider, wishlist, product-context), and small useSyncExternalStore stores such as the mini-cart store. Use for "where should this state live", "useBasket", "useAuth", "share state between components", "optimistic add to cart", "global client state", "hydration mismatch from a store", or "do I need Zustand". Do not use for fetching or mutating data (use `storefront-next:sfnext-data-fetching`), controlling loader re-runs (use `storefront-next:sfnext-revalidation`), or login flows and sessions (use `storefront-next:sfnext-authentication`).
# Storefront Next State Management Server state lives in loaders and actions. Client state is small and local. There is no global client store library in the template (Zustand was removed): use the tools below in this order. `docs/README-STATE.md` and `docs/README-DATA.md` in your project cover each in depth. ## Decision table | State | Use | |---|---| | Data from SCAPI | `loader` + `loaderData`; `action` for writes | | Another route's loader data | `useRouteLoaderData('routes/_app')` (route id = file path without extension) | | Filters, sort, pagination, tab | URL: `useSearchParams` / `useParams` (shareable, SSR-friendly) | | Must survive requests, read by server | cookies (`createCookie` from `@/lib/cookie-utils.server`) or session helpers | | Pending or optimistic result | `fetcher.formData`, `useNavigation`, `useOptimistic` | | Form fields, toggles, open/close in one component | `useState` / `useReducer` | | Shared by a subtree | React context with a provider at the owning layout | | Shared app-wide and read outside React, or high-frequency | module-level `useSyncExternalStore` store | | Request-scoped server values (site, auth, basket) | router `context` (middleware), read with `context.get(...)` | Prefer the earliest row that works. Never copy loader data into `useState` to "cache" it. ## Existing providers and stores | Need | API | |---|---| | Basket | `BasketProvider` in `src/providers/basket.tsx`: `useBasket({ autoLoad })`, `useBasketSnapshot`, `useBasketHydrated`, `useBasketError`, `useBasketUpdater`, `useBasketReset`, `useBasketLoader`, `useBasketReconcile`; `BasketCookieReconciler` syncs the cookie | | Session info | `useAuth()` from `@/providers/auth` | | Product / variant selection | `@/providers/product-context`, `@/providers/product-view` | | Image widths | `DynamicImageProvider` (`@/providers/dynamic-image`) | | Wishlist | `src/providers/wishlist.ts` (module-level store) | | Mini-cart open state | `useMiniCartStore`, `setMiniCartOpen` in `src/hooks/mini-cart-store.ts` | | Cart toast | `src/hooks/cart-mutation-toast-store.ts` | | Shopper context | `useShopperContext` (`@/hooks/use-shopper-context`) | The basket is loaded through `useScapiFetcher('shopperBasketsV2', 'getBasket')` and is updated from action results via `updateBasketResource` (server side, `@/middlewares/basket.server`), so after a basket action components simply re-render from `useBasket()`. The `__sfdc_basket` cookie carries the basket id; `__sfdc_usertype` is the user-type hint. Do not read these directly in components; use the hooks. Server reads: `getBasket`, `getBasketSnapshot`, `ensureBasketId`, `destroyBasket`, and `getAuth`, `updateAuth`, `destroyAuth` from `@/middlewares/auth.server`. ## Optimistic UI ```tsx const fetcher = useFetcher<typeof action>(); const removing = fetcher.formData?.get('itemId') === item.itemId; return <li hidden={removing}>...</li>; ``` Use `useOptimistic` for values the server will confirm, and `useNavigation` for URL-driven pending states. Roll back by reading the confirmed `loaderData`/basket value; never hand-maintain a parallel copy. ## A small external store Use when state is global, changes often, or must be set outside React. Follow `src/hooks/mini-cart-store.ts`: ```ts import { useSyncExternalStore } from 'react'; interface State { open: boolean } let state: State = { open: false }; const SERVER_STATE: State = Object.freeze({ open: false }); const listeners = new Set<() => void>(); const subscribe = (l: () => void) => { listeners.add(l); return () => void listeners.delete(l); }; export const setOpen = (open: boolean) => { if (open === state.open) return; // skip no-op writes state = { ...state, open }; listeners.forEach((l) => l()); }; export function useDrawerStore<T>(select: (s: State) => T): T { return useSyncExternalStore(subscribe, () => select(state), () => select(SERVER_STATE)); } ``` Rules: - `getSnapshot` must return a referentially stable value when nothing changed. Select primitives; a selector that allocates per call loops forever. - Provide a server snapshot and mutate only from events or effects so SSR and the first client render match (no hydration warnings). - Split stores by independent writers so each consumer subscribes to a slice. - For an instance-based store (one per widget), create it in a factory and provide it by context, as `createStoreLocatorStore` does in the store-locator extension. ## Context providers - Put the provider at the lowest layout that contains all consumers. - Memoize the `value` (`useMemo`, `useCallback`) and split read-only state from updaters into separate contexts so updater-only consumers do not re-render. - Use a selector pattern for large contexts (see README-STATE "Context Selector Pattern"). - Values used only inside callbacks belong in `useRef`, not context state. - Compose providers with `src/providers/compose-providers.tsx`. More patterns and anti-patterns in [PATTERNS.md](references/PATTERNS.md). ## Common mistakes | Mistake | Instead | |---|---| | `useEffect` that fetches on mount and stores in state | loader (or streamed promise) | | Copying `loaderData` or basket into `useState` | read directly; derive with plain expressions | | Filters in `useState` | `useSearchParams` | | Reading cookies in components | loader returns the value, or use the provider hook | | New context for one component's state | `useState` in that component | | Adding Zustand or Redux | the mechanisms above; check `storefront-next:sfnext-overview` for conventions | ## Finding more `AGENTS.md` ("Key Documentation"), `docs/README-STATE.md`, `docs/README-DATA.md`. `b2c docs search "storefront next state"` or the `docs_search` MCP tool. ## Related Skills - `storefront-next:sfnext-data-fetching` - loaders, actions, fetchers - `storefront-next:sfnext-revalidation` - when providers make a re-run unnecessary - `storefront-next:sfnext-performance` - hydration stability and render scope - `storefront-next:sfnext-authentication` - `useAuth` and session lifecycle - `storefront-next:sfnext-commerce-features` - cart, wishlist, checkout features - `storefront-next:sfnext-components` - component conventions
Auf GitHub ansehen