Broadleaf Microservices
  • v1.0.0-latest-prod

Application State Management

React State Management

For most of our state management we use the useState and useReducer React hooks and React Contexts. We have not found any need to use Redux for our state-management needs.

Persisting State in Next.js

Because Next.js does server-side rendering for us, if we want to persist any state between page renders, we will not be able to use the browser session or local storage like we could in a CRA. However, we can use cookies.

We use cookies to persist information such as the in-process cart’s ID and guest token, the current resolved Tenant and Application, the currently selected currency and locale, etc. so that this information is available when rendering pages on the server-side. The contents of these cookies are primarily managed by context provides like TenantProvider in app/common/contexts/tenant-context.tsx that initializes th TenantContext.

When managing cookies in the client side, we use the react-cookie library.

Before using any cookies, we need to wrap our App in the CookieProvider After that it’s simple: react-cookie provides a hook, useCookies to give us access to the cookies.

Client-Side Cookie Management Example
import { useCookies } from 'react-cookie';

import { CookieName } from '@app/common/utils';

const useCookieExample = (): void => {
  const [, setCookie, removeCookie] = useCookies([CookieName.CURRENCY]);

  // set cookie
  setCookie(CookieName.CURRENCY, 'en-US', {
    path: '/',
    maxAge: 3600,
    sameSite: 'strict',
  });

  // remove cookie
  removeCookie(CookieName.CURRENCY, { path: '/' })
};

On the server-side to access cookies, we can take the IncomingMessage passed as the req prop in our page’s getServerSideProps function and parse the cookie header (and fallback on the document.cookie if the header is empty) To aid in parsing the header, we use the cookie library.

Server-Side Cookie Access
import { GetServerSidePropsContext } from 'next';
import { IncomingMessage } from 'http';
import cookie from 'cookie';

type Cookie = {
  storedField: string;
};

type ServerSideProps = {
  storedField: string;
}

export async function getServerSideProps({
  query,
  req,
}: GetServerSidePropsContext): Promise<ServerSideProps> {
  const allCookies = cookie.parse(req.headers.cookie);
  const specificCookie = cookies['cookieName'] || '{}';
  return { props: { storedField: JSON.parse(specificCookie) } };
}

Tenant Resolution Cache

We’ve implemented a server side cache for tenant resolution results so that refreshing pages or navigation will not cause the tenant information to be fetched again.

We’ve set up an API Route in the Starter at pages/api/tenants/resultion.ts that receives requests to resolve the tenant info. This interacts first with a cache enabled in the custom Node server (custom-server/server.js) to see if we have previously cached it. Then, it will send a request to the Tenant Resolver Endpoint and cache the result or return a 404 or some error code as appropriate.

Cookies List

A list of custom cookie names is in cookie-utils.ts and can be imported with the CookieName constant. This list does not include cookies added by the backend Microservices, only those managed by the Next.js starter itself.

  • blAccountId: Stores the user-selected B2B account ID.

  • blAppUrl: Stores the URL used to resolve the tenant information in case using parameter-based resolution.

  • blCurrency: Stores the user-selected currency if the user is allowed to select the currency.

    • Not used by default.

  • blLocale: Stores the user-selected locale.

  • blSandboxPreview: Stores sandbox preview-on-site state across page renders.

    • Uses a cookie to allow server-side access.

  • blSubmittedCart: Used to store the email address used on a submitted cart after checkout to allow fetching an anonymous user’s cart to show on a confirmation page.

    • By default, submitted carts aren’t visible except for logged-in customers without providing the user’s email and order number. When we navigate to the confirmation page, we put the order number in the url, so we only need to store the email address to retrieve the submitted cart.