Skip to content

Commit

Permalink
feat(ust-1534): add global state manager (#7223)
Browse files Browse the repository at this point in the history
  • Loading branch information
dawid-ziobro authored Aug 6, 2024
1 parent dd4a13a commit 71e3cf8
Show file tree
Hide file tree
Showing 26 changed files with 1,829 additions and 118 deletions.
99 changes: 99 additions & 0 deletions .changeset/curly-years-help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
"@vue-storefront/next": major
---

[ADDED] global state management with Zustand. This will allow you to keep your global state in a more organized way.
It shares the data about:
- cart
- customer
- currency
- locale

This change will require you to refactor your hooks to make use of the introduced state manager.
As this is only a state management, you will still need to use the hooks to fetch the data and put it into the state.

To make use of the new state solution you will need to change the file `sdk/sdk-context.ts`.

```ts
// before
'use client';

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

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

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

```ts
// after
'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,
useSfCartState,
useSfCurrenciesState,
useSfCurrencyState,
useSfCustomerState,
useSfLocaleState,
useSfLocalesState,
} = createAlokaiContext<Sdk, SfContract>();
```

The type `SfContract` is a type that represents the contract between the middleware and the state manager.
It is delivered out of the box.

Example of usage:

```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>
);
}

```

[BREAKING] [CHANGED] the function `createSdkContext` exported from the `client` is changed to `createAlokaiContext`.
Also, it no longer returns an array with two elements, but an object with multiple properties.
This change is related to the fact that now it not only provide SDK context but also global state management context and hooks for handling it.

```diff
- import { createSdkContext } from '@vue-storefront/next/client';
+ import { createAlokaiContext } from '@vue-storefront/next/client';
```
61 changes: 61 additions & 0 deletions .changeset/giant-dogs-crash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
"@vue-storefront/nuxt": major
---

[ADDED] global state management with Pinia. This will allow you to keep your global state in a more organized way.
It shares the data about:
- cart
- customer
- currency
- locale

This change will require you to refactor your composables to make use of the introduced state manager.
As this is only a state management, you will still need to use the composables to fetch the data and put it into the state.

Every part of global state can now be used as refs so reading and writing to them is more straightforward.

Example of usage:

```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>
```

[BREAKING] [CHANGED] module configKey is changed from `vsf` to `alokai`. Also, the support for the `vsf` key in Runtime Envs has been changed to `alokai`.

```diff
meta: {
name: "@vue-storefront/nuxt",
- configKey: "vsf",
+ configKey: "alokai",
compatibility: {
nuxt: "^3.0.0",
},
```

```diff
nuxt.options.runtimeConfig.public.alokai = defu(
- nuxt.options.runtimeConfig.public?.vsf as any,
+ nuxt.options.runtimeConfig.public?.alokai as any,
options
);
```

Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,23 @@

import { useEffect, useState } from "react";
import { useSdk } from "../../sdk/sdk-provider";
import { useSfCurrencyState } from "@vue-storefront/next/client";

export function ClientComponentUsingSdk() {
const sdk = useSdk();
const [response, setResponse] = useState<any>();
const [currency] = useSfCurrencyState();

useEffect(() => {
sdk.example.getSuccess().then((res) => {
setResponse(res);
});
}, []);

return <pre>{JSON.stringify(response, null, 2)}</pre>;
return (
<>
<pre>{JSON.stringify(response, null, 2)}</pre>
<div>Currency from the state: {currency}</div>
</>
);
}
18 changes: 15 additions & 3 deletions packages/next/__tests__/apps/app-router/app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { ReactNode } from "react";
import { createSdk, type CreateSdkOptions } from "@vue-storefront/next";
import { SdkProvider } from "../sdk/sdk-provider";
import { AlokaiProvider } from "../sdk/sdk-provider";
import { getSdkConfig } from "../sdk/config";

export function Providers({
Expand All @@ -11,8 +11,20 @@ export function Providers({
}: {
children: ReactNode;
sdkOptions: CreateSdkOptions;
}) {
}) {
const { getSdk } = createSdk(sdkOptions, getSdkConfig());

return <SdkProvider sdk={getSdk()}>{children}</SdkProvider>;
return (
<AlokaiProvider
initialData={{
currencies: ["USD", "EUR"],
currency: "USD",
locale: "en",
locales: ["en", "de"],
}}
sdk={getSdk()}
>
{children}
</AlokaiProvider>
);
}
4 changes: 2 additions & 2 deletions packages/next/__tests__/apps/app-router/sdk/sdk-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"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>();
25 changes: 19 additions & 6 deletions packages/next/__tests__/apps/app-router/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -150,14 +150,15 @@
csstype "^3.0.2"

"@vue-storefront/next@file:../../..":
version "3.0.0"
version "3.0.1"
dependencies:
"@vue-storefront/sdk" "^3.1.0"
"@vue-storefront/sdk" "^3.1.1"
zustand "^4.5.4"

"@vue-storefront/sdk@^3.1.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@vue-storefront/sdk/-/sdk-3.1.0.tgz#e4f450c9f98ab90eb22c378809c8fe4083c76bdb"
integrity sha512-8txDG3CuDzE+3nxQpgcnvanusAdMiWZWxcNCPxXu7F4ux7WM6oZpzJ4UahSAtbExRUPfqdd92eC6yV+Ys41qtg==
"@vue-storefront/sdk@^3.1.1":
version "3.1.1"
resolved "https://registry.yarnpkg.com/@vue-storefront/sdk/-/sdk-3.1.1.tgz#56c428784938cc35a889d3b576db0fbf6799e209"
integrity sha512-rPLcdK0i9tDdFSnRsGFgPVF1nmMx5G5c2rc4uYJrkRwZw/cSLmmznXMLMejgnX1QrdAkfG0D4oSqO37tMCJUbg==

busboy@1.6.0:
version "1.6.0"
Expand Down Expand Up @@ -331,10 +332,22 @@ undici-types@~5.26.4:
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==

use-sync-external-store@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==

watchpack@2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
dependencies:
glob-to-regexp "^0.4.1"
graceful-fs "^4.1.2"

zustand@^4.5.4:
version "4.5.4"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.4.tgz#63abdd81edfb190bc61e0bbae045cc4d52158a05"
integrity sha512-/BPMyLKJPtFEvVL0E9E9BTUM63MNyhPGlvxk1XjrfWTUlV+BR8jufjsovHzrtR6YNcBEcL7cMHovL1n9xHawEg==
dependencies:
use-sync-external-store "1.2.0"
16 changes: 16 additions & 0 deletions packages/next/__tests__/apps/pages-router/_document.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Html, Head, Main, NextScript } from "next/document";
import { PublicEnvScript } from "next-runtime-env";

export default function Document() {
return (
<Html lang="en">
<Head>
<PublicEnvScript />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
44 changes: 22 additions & 22 deletions packages/next/__tests__/apps/pages-router/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
import type { AppProps } from "next/app";
import { createSdk } from "@vue-storefront/next";
import { SdkProvider } from "../sdk/sdk-provider";
import { AlokaiProvider } from "../sdk/sdk-provider";
import { getSdkOptions } from "../sdk/options";
import { getSdkConfig } from "../sdk/config";
import { PublicEnvScript } from "next-runtime-env";

export default function App({ Component, pageProps }: AppProps) {
const { getSdk } = createSdk(getSdkOptions(), getSdkConfig());

return (
<html>
<head>
<PublicEnvScript />
</head>
<body>
<SdkProvider sdk={getSdk()}>
<nav>
<ul>
<li>
<a href="/ssr">SSR Page</a>
</li>
<li>
<a href="/csr">CSR Page</a>
</li>
</ul>
</nav>
<Component {...pageProps} />
</SdkProvider>
</body>
</html>
<AlokaiProvider
initialData={{
currencies: ["USD", "EUR"],
currency: "USD",
locale: "en",
locales: ["en", "de"],
}}
sdk={getSdk()}
>
<nav>
<ul>
<li>
<a href="/ssr">SSR Page</a>
</li>
<li>
<a href="/csr">CSR Page</a>
</li>
</ul>
</nav>
<Component {...pageProps} />
</AlokaiProvider>
);
}
3 changes: 3 additions & 0 deletions packages/next/__tests__/apps/pages-router/pages/csr.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { useEffect, useState } from "react";
import { useSdk } from "../sdk/sdk-provider";
import { useSfCurrencyState } from "@vue-storefront/next/client";

export default function ClientPage() {
const sdk = useSdk();
const [result, setResult] = useState();
const [currency] = useSfCurrencyState();

useEffect(() => {
sdk.example.getSuccess().then((res) => {
Expand All @@ -15,6 +17,7 @@ export default function ClientPage() {
<main>
<h1>Client Side Page</h1>
<pre>{JSON.stringify(result, null, 2)}</pre>
<div>Currency from the state: {currency}</div>
</main>
);
}
3 changes: 3 additions & 0 deletions packages/next/__tests__/apps/pages-router/pages/ssr.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { GetServerSideProps } from "next";
import { getSdk } from "../sdk/sdk.server";
import { useSfCurrencyState } from "@vue-storefront/next/client";

export default function SsrPage({ result }: any) {
const [currency] = useSfCurrencyState();
return (
<main>
<h1>Server Side Page</h1>
<pre>{JSON.stringify(result, null, 2)}</pre>
<div>Currency from the state: {currency}</div>
</main>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"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>();
Loading

0 comments on commit 71e3cf8

Please sign in to comment.