Skip to content

Commit e29321a

Browse files
committed
Replace Context with Zustand
1 parent 82621f3 commit e29321a

File tree

12 files changed

+159
-179
lines changed

12 files changed

+159
-179
lines changed

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,17 @@ The current release has been tested and is confirmed working with the following
7979
- Tests with Playwright
8080
- Connect to Woocommerce GraphQL API and list name, price and display image for products
8181
- Support for simple products and variable products
82-
- Cart handling and checkout with WooCommerce (Cash On Delivery only for now)
82+
- Cart handling and checkout with WooCommerce using Zustand for state management
83+
- Persistent cart state with localStorage sync
84+
- Efficient updates through selective subscriptions
85+
- Type-safe cart operations
86+
- Cash On Delivery payment method
8387
- Algolia search (requires [algolia-woo-indexer](https://github.com/w3bdesign/algolia-woo-indexer))
8488
- Meets WCAG accessibility standards where possible
8589
- Placeholder for products without images
86-
- Apollo Client with GraphQL
90+
- State Management:
91+
- Zustand for global state management
92+
- Apollo Client with GraphQL
8793
- React Hook Form
8894
- Native HTML5 form validation
8995
- Animations with Framer motion, Styled components and Animate.css

package-lock.json

Lines changed: 63 additions & 47 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,12 @@
3636
"next": "15.3.0",
3737
"nprogress": "^0.2.0",
3838
"postcss": "^8.5.3",
39-
"react": "18.3.1",
40-
"react-dom": "18.3.1",
39+
"react": "19.1.0",
40+
"react-dom": "19.1.0",
4141
"react-hook-form": "^7.55.0",
4242
"react-instantsearch-dom": "^6.40.4",
43-
"uuid": "^11.1.0"
43+
"uuid": "^11.1.0",
44+
"zustand": "^5.0.3"
4445
},
4546
"devDependencies": {
4647
"@lhci/cli": "^0.14.0",
@@ -52,7 +53,7 @@
5253
"@types/uuid": "^10.0.0",
5354
"@typescript-eslint/eslint-plugin": "^8.29.1",
5455
"@typescript-eslint/parser": "^8.29.1",
55-
"babel-plugin-styled-components": "^2.1.4",
56+
"babel-plugin-styled-components": "^2.1.4",
5657
"eslint-config-next": "^15.3.0",
5758
"postcss-preset-env": "^10.1.5",
5859
"prettier": "^3.5.3",

src/components/Cart/CartContents.component.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { useContext, useEffect } from 'react';
1+
import { useEffect } from 'react';
22
import { useMutation, useQuery } from '@apollo/client';
33
import Link from 'next/link';
44
import Image from 'next/image';
55
import { useRouter } from 'next/router';
66
import { v4 as uuidv4 } from 'uuid';
77

8-
import { CartContext } from '@/stores/CartProvider';
8+
import { useCartStore } from '@/stores/cartStore';
99
import Button from '@/components/UI/Button.component';
1010
import LoadingSpinner from '../LoadingSpinner/LoadingSpinner.component';
1111

@@ -21,20 +21,22 @@ import { UPDATE_CART } from '@/utils/gql/GQL_MUTATIONS';
2121

2222
const CartContents = () => {
2323
const router = useRouter();
24-
const { setCart } = useContext(CartContext);
24+
const { setCart } = useCartStore();
2525
const isCheckoutPage = router.pathname === '/kasse';
2626

2727
const { data, refetch } = useQuery(GET_CART, {
2828
notifyOnNetworkStatusChange: true,
2929
onCompleted: () => {
3030
const updatedCart = getFormattedCart(data);
31-
if (!updatedCart && !data.cart.contents.nodes.length) {
31+
if (!updatedCart && !data?.cart?.contents?.nodes?.length) {
3232
localStorage.removeItem('woocommerce-cart');
3333
setCart(null);
3434
return;
3535
}
3636
localStorage.setItem('woocommerce-cart', JSON.stringify(updatedCart));
37-
setCart(updatedCart);
37+
if (updatedCart) {
38+
setCart(updatedCart);
39+
}
3840
},
3941
});
4042

src/components/Checkout/CheckoutForm.component.tsx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*eslint complexity: ["error", 20]*/
22
// Imports
3-
import { useState, useContext, useEffect } from 'react';
3+
import { useState, useEffect } from 'react';
44
import { useQuery, useMutation, ApolloError } from '@apollo/client';
55

66
// Components
@@ -11,7 +11,7 @@ import LoadingSpinner from '../LoadingSpinner/LoadingSpinner.component';
1111
// GraphQL
1212
import { GET_CART } from '@/utils/gql/GQL_QUERIES';
1313
import { CHECKOUT_MUTATION } from '@/utils/gql/GQL_MUTATIONS';
14-
import { CartContext } from '@/stores/CartProvider';
14+
import { useCartStore } from '@/stores/cartStore';
1515

1616
// Utils
1717
import {
@@ -51,7 +51,7 @@ export interface ICheckoutData {
5151
}
5252

5353
const CheckoutForm = () => {
54-
const { cart, setCart } = useContext(CartContext);
54+
const { cart, setCart } = useCartStore();
5555
const [orderData, setOrderData] = useState<ICheckoutData | null>(null);
5656
const [requestError, setRequestError] = useState<ApolloError | null>(null);
5757
const [orderCompleted, setorderCompleted] = useState<boolean>(false);
@@ -63,17 +63,19 @@ const CheckoutForm = () => {
6363
// Update cart in the localStorage.
6464
const updatedCart = getFormattedCart(data);
6565

66-
if (!updatedCart && !data.cart.contents.nodes.length) {
66+
if (!updatedCart && !data?.cart?.contents?.nodes?.length) {
6767
localStorage.removeItem('woo-session');
68-
localStorage.removeItem('wooocommerce-cart');
68+
localStorage.removeItem('woocommerce-cart');
6969
setCart(null);
7070
return;
7171
}
7272

7373
localStorage.setItem('woocommerce-cart', JSON.stringify(updatedCart));
7474

75-
// Update cart data in React Context.
76-
setCart(updatedCart);
75+
// Update cart data in Zustand store
76+
if (updatedCart) {
77+
setCart(updatedCart);
78+
}
7779
},
7880
});
7981

src/components/Header/Cart.component.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { useContext, useState, useEffect } from 'react';
1+
import { useState, useEffect } from 'react';
22
import Link from 'next/link';
33

4-
import { CartContext } from '@/stores/CartProvider';
4+
import { useCartStore } from '@/stores/cartStore';
55

66
interface ICartProps {
77
stickyNav?: boolean;
@@ -12,7 +12,7 @@ interface ICartProps {
1212
* Displays amount of items in cart.
1313
*/
1414
const Cart = ({ stickyNav }: ICartProps) => {
15-
const { cart } = useContext(CartContext);
15+
const cart = useCartStore((state) => state.cart);
1616
const [productCount, setProductCount] = useState<number | null | undefined>();
1717

1818
useEffect(() => {

src/components/Layout/Layout.component.tsx

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Imports
2-
import { ReactNode, useContext, useEffect } from 'react';
2+
import React, { ReactNode, useEffect } from 'react';
33
import { useQuery } from '@apollo/client';
44

55
// Components
@@ -9,7 +9,7 @@ import Footer from '@/components/Footer/Footer.component';
99
import Stickynav from '@/components/Footer/Stickynav.component';
1010

1111
// State
12-
import { CartContext } from '@/stores/CartProvider';
12+
import { useCartStore } from '@/stores/cartStore';
1313

1414
// Utils
1515
import { getFormattedCart } from '@/utils/functions/functions';
@@ -31,23 +31,18 @@ interface ILayoutProps {
3131
*/
3232

3333
const Layout = ({ children, title }: ILayoutProps) => {
34-
const { setCart } = useContext(CartContext);
34+
const { updateCart } = useCartStore();
3535

3636
const { data, refetch } = useQuery(GET_CART, {
3737
notifyOnNetworkStatusChange: true,
3838
onCompleted: () => {
39-
// Update cart in the localStorage.
4039
const updatedCart = getFormattedCart(data);
41-
42-
if (!updatedCart && !data?.cart?.contents?.nodes.length) {
43-
// Should we clear the localStorage if we have no remote cart?
44-
return;
40+
41+
if (updatedCart) {
42+
// Update cart in localStorage and Zustand store
43+
localStorage.setItem('woocommerce-cart', JSON.stringify(updatedCart));
44+
updateCart(updatedCart);
4545
}
46-
47-
localStorage.setItem('woocommerce-cart', JSON.stringify(updatedCart));
48-
49-
// Update cart data in React Context.
50-
setCart(updatedCart);
5146
},
5247
});
5348

src/components/Product/AddToCart.component.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { v4 as uuidv4 } from 'uuid';
77
import Button from '@/components/UI/Button.component';
88

99
// State
10-
import { CartContext } from '@/stores/CartProvider';
10+
import { useCartStore } from '@/stores/cartStore';
1111

1212
// Utils
1313
import { getFormattedCart } from '@/utils/functions/functions';
@@ -96,7 +96,7 @@ const AddToCart = ({
9696
variationId,
9797
fullWidth = false,
9898
}: IProductRootObject) => {
99-
const { setCart, isLoading: isCartLoading } = useContext(CartContext);
99+
const { setCart, isLoading: isCartLoading } = useCartStore();
100100
const [requestError, setRequestError] = useState<boolean>(false);
101101

102102
const productId = product?.databaseId ? product?.databaseId : variationId;
@@ -119,7 +119,7 @@ const AddToCart = ({
119119

120120
localStorage.setItem('woocommerce-cart', JSON.stringify(updatedCart));
121121

122-
// Update cart data in React Context.
122+
// Update cart data in Zustand store
123123
setCart(updatedCart);
124124
},
125125
});

0 commit comments

Comments
 (0)