Skip to content

Commit

Permalink
docs: add info about state management (#7232)
Browse files Browse the repository at this point in the history
  • Loading branch information
dawid-ziobro authored Sep 24, 2024
1 parent db35876 commit d8500a8
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 75 deletions.
4 changes: 4 additions & 0 deletions docs/content/4.sdk/1.index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ By utilizing the Alokai SDK, you can establish a type-safe contract between your

:card{to="/sdk/getting-started" title="Get Started" description="Learn how to initialize the SDK in your storefronts" icon="ic:baseline-rocket"}

Along with the Alokai SDK, we're providing tools for the State Management. Those are already preconfigured and ready to use. Available both for Next.js and Nuxt through our dedicated packages.

:card{to="/sdk/getting-started/state-management" title="State Management" description="Learn how to use built in State Manager" icon="grommet-icons:storage"}


## Key Features

Expand Down
78 changes: 40 additions & 38 deletions docs/content/4.sdk/2.getting-started/1.index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ If you're setting your Alokai application from scratch, you'll need to configure
In the examples below, we assume that you have an Alokai app with the Unified Data Model. However, the approach for non-unified Alokai applications is similar.
:::

There are various ways to configure the SDK, depending on your chosen framework. For Next.js and Nuxt, you can use the `@vue-storefront/next` and `@vue-storefront/nuxt` packages respectively. If you're looking for framework agnostic experience, you can use the `@vue-storefront/sdk` package.
There are various ways to configure the SDK, depending on your chosen framework. For Next.js and Nuxt, you can use the `@vue-storefront/next` and `@vue-storefront/nuxt` packages respectively.
These packages also provide tools for handling the global state management.

If you're looking for framework agnostic experience, you can use the `@vue-storefront/sdk` package.

::tabs{:titles='["Next.js", "Nuxt", "Other"]' class="mt-8"}

Expand Down Expand Up @@ -67,7 +70,7 @@ export function getSdkOptions() {
}
```

3. Create SDK Config file - `sdk.config.ts`. In this file, we define the configuration of different modules. We are making it a separate file to easily import it both on the server and the client. Create the SDK configuration by importing the `createSdk` function from the Next.js SDK and using the `middlewareModule` it provides. You should also import other modules you want to use.
3. Create SDK Config file - `config.ts`. In this file, we define the configuration of different modules. We are making it a separate file to easily import it both on the server and the client. Create the SDK configuration by importing the `createSdk` function from the Next.js SDK and using the `middlewareModule` it provides. You should also import other modules you want to use.

```ts [sdk/config.ts]
import { contentfulModule } from "@vsf-enterprise/contentful-sdk";
Expand Down Expand Up @@ -159,45 +162,35 @@ import { getSdk } from "@/sdk/sdk.server";
const sdk = getSdk();
```

### createSdkContext
### createAlokaiContext

For client-side rendering, you can use `createAlokaiContext`. This function serves for two purposes:
- providing the SDK context
- providing the global state management context and hooks for handling the state of the application

For client-side rendering, you can use `createSdkContext`. To use it, you'll need to create a new file in your application, for example `sdk/sdk-provider.tsx`:
To use it, you'll need to create a new file in your application, for example `sdk/alokai-context.tsx`:

```ts
'use client';

import { createSdkContext } from '@vue-storefront/next/client';
import { createAlokaiContext } from '@vue-storefront/next/client';

import type { Sdk } from './sdk.server';

export const [SdkProvider, useSdk] = createSdkContext<Sdk>();
export const { AlokaiProvider, useSdk } = createAlokaiContext<Sdk>();
```

Once you have created the SDK context, you can create client-side SDK instance and register it in your application. For example, if you're using the Pages Router, you can register it in `pages/_app.tsx`:
This is also a place where you can import hooks for handling the state management. You can read more about the state management in the [State Management](/sdk/getting-started/state-management) page.

```tsx
import type { AppProps } from "next/app";
import { SdkProvider } from "@/sdk/sdk-provider";
import { getSdkOptions } from "@/sdk/options";
import { getSdkConfig } from "@/sdk/config";
Once you have created the Alokai context, you can create client-side SDK instance and register it in your application.

export default function App({ Component, pageProps }: AppProps) {
const { getSdk } = createSdk(
getSdkOptions(),
getSdkConfig()
);
You can do it in two steps:

return (
<SdkProvider sdk={getSdk()}>
<Component {...pageProps} />
</SdkProvider>
);
}
```
1. Retrieve the SDK config in the server component in `app/[locale]/layout.tsx`:

If you're using the App Router, you can register it in `app/layout.tsx`:
```tsx
// app/[locale]/layout.tsx

```tsx [app/layout.tsx]
import { ReactNode } from "react";
import { PublicEnvProvider } from "next-runtime-env";
import { Providers } from "./providers";
Expand All @@ -218,7 +211,11 @@ export default function RootLayout({ children }: { children: ReactNode }) {
}
```

```tsx [app/providers.tsx]
2. Pass the SDK options to the `Providers` client component and initialize the SDK instance along with the `AlokaiProvider`:

```tsx
// components/providers.tsx

"use client";

import { ReactNode } from "react";
Expand All @@ -232,13 +229,16 @@ export function Providers({ children, sdkOptions }: { children: ReactNode, sdkOp
sdkOptions,
getSdkConfig()
);

return <SdkProvider sdk={getSdk()}>{children}</SdkProvider>;
return (
<AlokaiProvider sdk={getSdk()}>
{children}
</AlokaiProvider>
)
}
```

::info
Don't be alarmed if you see a `use client` directive in the `app/providers.tsx` file. This will not turn your application into a client-side rendered application. All children inside the provider will be still rendered on the server-side by default. You can read more about `use client` directive in [React Documentation](https://react.dev/reference/react/use-client).
Don't be alarmed if you see a `use client` directive in the `components/providers.tsx` file. This will not turn your application into a client-side rendered application. All children inside the provider will be still rendered on the server-side by default. You can read more about `use client` directive in [React Documentation](https://react.dev/reference/react/use-client).
::

## Usage
Expand All @@ -250,7 +250,7 @@ Once you have registered the SDK in your application, you can start using it. He
```tsx [Pages Router]
import { getSdk } from "@/sdk";

export function getServersideProps() {
export async function getServersideProps() {
const sdk = getSdk();
const { products } = await sdk.commerce.searchProduct();

Expand Down Expand Up @@ -302,7 +302,7 @@ That's it! You can now use VueStorefront SDK Module in your Next.js app ✨

## Installation

To get started with the SDK within Next.js, first you have to install and configure the `@vue-storefront/nuxt` module.
To get started with the SDK within Nuxt, first you have to install and configure the `@vue-storefront/nuxt` module.

1. In the root of your Storefront project run:

Expand Down Expand Up @@ -373,7 +373,7 @@ export default defineSdkConfig(

Let's break down the code above:

The `defineSdkConfig` function is used for intializing the SDK. The parameter for calling this function should be an anonymous function that receives an injected context from the module, containing:
The `defineSdkConfig` function is used for initializing the SDK. The parameter for calling this function should be an anonymous function that receives an injected context from the module, containing:

- the `buildModule` function,
- the configuration object that contains data useful in module configuration such as: middleware URL (`middlewareUrl`), or cache busting identifier (`cdnCacheBustingId`)
Expand All @@ -396,7 +396,7 @@ const { data: products } = await useAsyncData("products", () =>
</script>
```

Code above is just an example of how you can use the SDK in your application. For more information about the avaialble methods, please refer to the respective [Integration's documentation](/integrations).
Code above is just an example of how you can use the SDK in your application. For more information about the available methods, please refer to the respective [Integration's documentation](/integrations).

That's it! You can now use VueStorefront SDK Module in your Nuxt app ✨

Expand Down Expand Up @@ -513,7 +513,7 @@ const { products } = await sdk.commerce.searchProduct();

::

Code above is just an example of how you can use the SDK in your application. For more information about the avaialble methods, please refer to the respective [Integration's documentation](/integrations).
Code above is just an example of how you can use the SDK in your application. For more information about the available methods, please refer to the respective [Integration's documentation](/integrations).

That's it! You can now use VueStorefront SDK Module in any JavaScript app ✨

Expand All @@ -523,12 +523,14 @@ That's it! You can now use VueStorefront SDK Module in any JavaScript app ✨

::grid{:columns="2"}
#section-1
:card{to="/sdk/advanced/extending-module" title="Extending the SDK" description="Learn how to customize, override, or extend any default functionality in the SDK" icon="ri:terminal-box-fill"}
:card{to="/sdk/getting-started/state-management" title="State Management" description="Learn how to use built in State Manager" icon="grommet-icons:storage"}
#section-2
:card{to="/sdk/advanced/middleware-module" title="The middlewareModule" description="Understand how to use the default SDK module" icon="fa6-solid:layer-group"}
:card{to="/sdk/advanced/extending-module" title="Extending the SDK" description="Learn how to customize, override, or extend any default functionality in the SDK" icon="ri:terminal-box-fill"}
#section-3
:card{to="/sdk/advanced/custom-modules" title="Create a custom SDK module" description="Create your own custom SDK module" icon="fluent:puzzle-cube-piece-20-filled"}
:card{to="/sdk/advanced/middleware-module" title="The middlewareModule" description="Understand how to use the default SDK module" icon="fa6-solid:layer-group"}
#section-4
:card{to="/sdk/advanced/custom-modules" title="Create a custom SDK module" description="Create your own custom SDK module" icon="fluent:puzzle-cube-piece-20-filled"}
#section-5
:card{to="/integrations" title="View all Integrations" description="View our ready-to-use integrations for popular e-commerce services" icon="fluent:puzzle-cube-piece-20-filled"}

::
185 changes: 185 additions & 0 deletions docs/content/4.sdk/2.getting-started/2.state-management.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# State Management

We offer ready-to-use tools for handling global state management tailored for both Next.js and Nuxt3 frameworks through our dedicated packages: `@vue-storefront/next` and `@vue-storefront/nuxt`.

For Next.js, we utilize [Zustand](https://zustand.docs.pmnd.rs/), a small, fast, and scalable state-management solution. For Nuxt3, we leverage [Pinia](https://pinia.vuejs.org/), a state management library that is intuitive and integrates seamlessly with Vue 3. These tools are designed to simplify state management in your applications, providing robust and efficient solutions out of the box.

Our state management solutions store core data such as:
- customer
- cart
- currently selected currency
- available currencies
- currently selected locale
- available locales

They also provide methods for reading and updating this data, making it easy to manage and access global state across your application.

::tabs{:titles='["Next.js", "Nuxt"]' class="mt-8"}

#tab-1

## Installation

To use the State Manager within Next.js, you need to have the `@vue-storefront/next` package installed. This step is already done if you have followed the [getting started](/sdk/getting-started). If not, you'll find installation instructions there.

## Usage

To use the state management, you need to extend the `alokai-context.tsx` file with the state management part.
All of the hooks that are returned from the `createAlokaiContext` function are straightforward to use. They follow the React's `useState` pattern, so you can read and write to the state like you would with the `useState`.

```ts
// sdk/alokai-context.tsx
'use client';

import { createAlokaiContext } from '@vue-storefront/next/client';
import type { SfContract } from 'storefront-middleware/types';

import type { Sdk } from './sdk.server';

export const { AlokaiProvider, useSdk } = createAlokaiContext<Sdk>(); // [!code --]
// [!code ++:11]
export const {
AlokaiProvider,
useSdk,
useSfCartState,
useSfCurrenciesState,
useSfCurrencyState,
useSfCustomerState,
useSfLocaleState,
useSfLocalesState,
} = createAlokaiContext<Sdk, SfContract>();
```

The `SfContract` interface is used to define the contract between the SDK and the state management. It contains the types for:
- cart
- customer
- currency
- locale

This is needed to ensure that the state management is aware of the types that you have within the middleware, as those types can be changed within the middleware.

## Initial data

There is also an option to pass initial data to the `AlokaiProvider`. This can be useful when you want to set the initial state of the application. You can pass the initial currencies, currency, locale, and locales to the `AlokaiProvider`. To do this, you need to pass the `initialData` prop to the `AlokaiProvider` component.

```tsx
// components/providers.tsx

"use client";

import type { ReactNode } from "react";
import type { CreateSdkOptions } from '@vue-storefront/next';
import { SdkProvider } from "@/sdk";
import { getSdkOptions } from "@/sdk/options";
import { getSdkConfig } from "@/sdk/config";

export function Providers({ children, sdkOptions }: { children: ReactNode, sdkOptions: CreateSdkOptions }) {
const { getSdk } = createSdk(
sdkOptions,
getSdkConfig()
);
return (
<AlokaiProvider sdk={getSdk()}> // [!code --]
// [!code ++:10]
<AlokaiProvider
initialData={{
currencies: ['USD', 'EUR'],
currency: 'USD',
locale: 'en',
locales: ['en', 'de'],
}}
sdk={getSdk()}
>
{children}
</AlokaiProvider>
)
}
```

Note that the `Providers` component is a client component. If you want to fetch some data on the server side and pass it to the client component you can fetch the data in the server component (for Alokai Storefront it will be in the `app/[locale]/layout.tsx` file) and pass it to the client component through props.


## Usage

The state is being updated on the Storefront side. State handling is already provided with the App Router based Alokai Storefront. You can use the state management in your components or hooks and modify the state as needed.
Here is an example of how you can use the state management in your components:

```tsx
import { useQuery } from "@tanstack/react-query";
import {
useSdk,
useSfCartState,
useSfCustomerState,
useSfCurrencyState,
useSfLocaleState,
} from "@/sdk/alokai-context";

function Component() {
const sdk = useSdk();
const [cart, setCart] = useSfCartState();
const [customer] = useSfCustomerState();
const [currency] = useSfCurrencyState();
const [locale] = useSfLocaleState();

const result = useQuery({
queryFn: () => sdk.unified.getCart(),
queryKey: ["cart", "main"],
});
// updating the cart state
useEffect(() => {
setCart(result.data);
}, [result.data]);

return (
<div>
<p>Cart total: {cart.total}</p>
<p>
Customer name: {customer.firstName} {customer.lastName}
</p>
<p>Currency: {currency}</p>
<p>Locale: {locale}</p>
</div>
);
}
```

The same approach can be used in hooks or any other components.

#tab-2

## Installation

To use the State Manager within Nuxt, you need to have the `@vue-storefront/nuxt` package installed. This step is already done if you have followed the [getting started](/sdk/getting-started). If not, you'll find installation instructions there.

## Usage

To use it you don't need to do anything special. Just use the auto-imported `useSfState` composable in your components or composables. You can use parts of the state as refs so you can read and write to them easily. As mentioned before, the state is based on Pinia. To read more on how to use it, please refer to the [official documentation](https://pinia.vuejs.org/).

Example usage of state management:

```vue
<template>
<div>
<p>Cart total: {{ cart.total }}</p>
<p>Customer name: {{ customer.firstName }} {{ customer.lastName }}</p>
<p>Currency: {{ currency }}</p>
<p>Locale: {{ locale }}</p>
</div>
</template>

<script setup>
const { cart, customer, currency, currencies, locale, locales } = storeToRefs(useSfState());

// updating the currency state
currency.value = "USD";

// updating the cart state
onMounted(async () => {
cart.value = await useSdk().unified.getCart();
});
</script>
```

The same approach can be used in composables or any other components.

::
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ In Nuxt, `middlewareModule` is available in the `defineSdkConfig` function.
import { UnifiedEndpoints } from "storefront-middleware/types";

export default defineSdkConfig(
({ buildModule, config, middlewareModule getRequestHeaders }) => ({
({ buildModule, config, middlewareModule, getRequestHeaders }) => ({
commerce: buildModule(middlewareModule<UnifiedEndpoints>, {
apiUrl: config.middlewareUrl + "/commerce", // SAP Commerce Cloud integration is available at /commerce endpoint
defaultRequestConfig: {
Expand Down
Loading

0 comments on commit d8500a8

Please sign in to comment.