Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 4d57cdb

Browse files
Merge pull request #1434 from w3bdesign/1433-add-zustand-for-replacement-of-usestate
1433 add zustand for replacement of usestate
2 parents 0db0d3e + ae4f59c commit 4d57cdb

File tree

14 files changed

+2269
-1784
lines changed

14 files changed

+2269
-1784
lines changed

‎package-lock.json‎

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

‎package.json‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
"license": "ISC",
2525
"dependencies": {
2626
"@apollo/client": "^3.12.11",
27-
"@types/react": "^19.0.8",
2827
"algoliasearch": "^4.24.0",
2928
"autoprefixer": "^10.4.20",
3029
"framer-motion": "12.4.2",
@@ -37,19 +36,22 @@
3736
"react-dom": "18.3.1",
3837
"react-hook-form": "^7.54.2",
3938
"react-instantsearch-dom": "^6.40.4",
40-
"uuid": "^11.0.5"
39+
"uuid": "^11.0.5",
40+
"zustand": "^5.0.3"
4141
},
4242
"devDependencies": {
4343
"@playwright/test": "^1.50.1",
4444
"@types/lodash": "^4.17.15",
45-
"@types/node": "22.13.1",
45+
"@types/node": "^22.13.1",
4646
"@types/nprogress": "^0.2.3",
47+
"@types/react": "^19.0.8",
48+
"@types/react-dom": "^19.0.3",
4749
"@types/react-instantsearch-dom": "^6.12.8",
4850
"@types/uuid": "^10.0.0",
49-
"@typescript-eslint/eslint-plugin": "^8.24.0",
50-
"@typescript-eslint/parser": "^8.24.0",
51-
"babel-plugin-styled-components": "^2.1.4",
52-
"eslint-config-next": "^15.1.7",
51+
"@typescript-eslint/eslint-plugin": "^8.23.0",
52+
"@typescript-eslint/parser": "^8.23.0",
53+
"babel-plugin-styled-components": "^2.1.4",
54+
"eslint-config-next": "^15.1.6",
5355
"postcss-preset-env": "^10.1.3",
5456
"prettier": "^3.5.0",
5557
"tailwindcss": "^3.4.17",

‎src/components/Cart/CartContents.component.tsx‎

Lines changed: 47 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,157 +1,123 @@
1-
import { useContext,useEffect } from 'react';
1+
import { ChangeEvent } 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,{RootObject,Product} from '@/stores/cart';
99
import Button from '@/components/UI/Button.component';
10-
import LoadingSpinner from '../LoadingSpinner/LoadingSpinner.component';
1110

1211
import {
1312
getFormattedCart,
14-
getUpdatedItems,
1513
handleQuantityChange,
16-
IProductRootObject,
1714
} from '@/utils/functions/functions';
1815

1916
import { GET_CART } from '@/utils/gql/GQL_QUERIES';
2017
import { UPDATE_CART } from '@/utils/gql/GQL_MUTATIONS';
2118

2219
const CartContents = () => {
2320
const router = useRouter();
24-
const { setCart } = useContext(CartContext);
21+
const { cart,setCart } = useCartStore();
2522
const isCheckoutPage = router.pathname === '/kasse';
2623

27-
const{ data, refetch }=useQuery(GET_CART, {
24+
useQuery(GET_CART, {
2825
notifyOnNetworkStatusChange: true,
29-
onCompleted: () => {
30-
const updatedCart = getFormattedCart(data);
31-
if (!updatedCart && !data.cart.contents.nodes.length) {
32-
localStorage.removeItem('woocommerce-cart');
33-
setCart(null);
34-
return;
26+
onCompleted: (data) => {
27+
// Only update if there's a significant difference to avoid unnecessary re-renders
28+
const updatedCart = getFormattedCart(data) as RootObject | undefined;
29+
if (!cart || cart.totalProductsCount !== updatedCart?.totalProductsCount) {
30+
setCart(updatedCart || null);
3531
}
36-
localStorage.setItem('woocommerce-cart', JSON.stringify(updatedCart));
37-
setCart(updatedCart);
3832
},
3933
});
4034

41-
const [updateCart, { loading: updateCartProcessing }] = useMutation(
42-
UPDATE_CART,
43-
{
44-
onCompleted: () => {
45-
refetch();
46-
setTimeout(() => {
47-
refetch();
48-
}, 3000);
49-
},
50-
},
51-
);
52-
53-
const handleRemoveProductClick = (
54-
cartKey: string,
55-
products: IProductRootObject[],
56-
) => {
57-
if (products?.length) {
58-
const updatedItems = getUpdatedItems(products, 0, cartKey);
59-
updateCart({
60-
variables: {
61-
input: {
62-
clientMutationId: uuidv4(),
63-
items: updatedItems,
64-
},
65-
},
66-
});
67-
}
68-
refetch();
69-
setTimeout(() => {
70-
refetch();
71-
}, 3000);
72-
};
73-
74-
useEffect(() => {
75-
refetch();
76-
}, [refetch]);
35+
const [updateCart] = useMutation(UPDATE_CART);
7736

78-
const cartTotal = data?.cart?.total || '0';
37+
const handleRemoveProductClick = (cartKey: string) => {
38+
// Update local state
39+
useCartStore.getState().removeProduct(cartKey);
7940

80-
const getUnitPrice = (subtotal: string, quantity: number) => {
81-
const numericSubtotal = parseFloat(subtotal.replace(/[^0-9.-]+/g, ''));
82-
return isNaN(numericSubtotal)
83-
? 'N/A'
84-
: (numericSubtotal / quantity).toFixed(2);
41+
// Update remote state in background
42+
updateCart({
43+
variables: {
44+
input: {
45+
clientMutationId: uuidv4(),
46+
items: [{
47+
key: cartKey,
48+
quantity: 0
49+
}],
50+
},
51+
},
52+
});
8553
};
8654

8755
return (
8856
<div className="container mx-auto px-4 py-8">
89-
{data?.cart?.contents?.nodes?.length ? (
57+
{cart?.products?.length ? (
9058
<>
9159
<div className="bg-white rounded-lg p-6 mb-8 md:w-full">
92-
{data.cart.contents.nodes.map((item: IProductRootObject) => (
60+
{cart.products.map((item: Product) => (
9361
<div
94-
key={item.key}
62+
key={item.cartKey}
9563
className="flex items-center border-b border-gray-200 py-4"
9664
>
9765
<div className="flex-shrink-0 w-24 h-24 relative hidden md:block">
9866
<Image
99-
src={
100-
item.product.node.image?.sourceUrl || '/placeholder.png'
101-
}
102-
alt={item.product.node.name}
67+
src={item.image?.sourceUrl || '/placeholder.png'}
68+
alt={item.name}
10369
layout="fill"
10470
objectFit="cover"
10571
className="rounded"
10672
/>
10773
</div>
10874
<div className="flex-grow ml-4">
10975
<h2 className="text-lg font-semibold">
110-
{item.product.node.name}
76+
{item.name}
11177
</h2>
11278
<p className="text-gray-600">
113-
kr {getUnitPrice(item.subtotal,item.quantity)}
79+
kr {item.price}
11480
</p>
11581
</div>
11682
<div className="flex items-center">
11783
<input
11884
type="number"
11985
min="1"
120-
value={item.quantity}
121-
onChange={(event) => {
86+
value={item.qty}
87+
onChange={(event: ChangeEvent<HTMLInputElement>) => {
88+
const newQty = parseInt(event.target.value, 10);
89+
if (isNaN(newQty) || newQty < 1) return;
90+
91+
// Update local state
92+
useCartStore.getState().updateProductQuantity(item.cartKey, newQty);
93+
94+
// Update remote state in background
12295
handleQuantityChange(
12396
event,
124-
item.key,
125-
data.cart.contents.nodes,
126-
updateCart,
127-
updateCartProcessing,
97+
item.cartKey,
98+
newQty,
99+
updateCart
128100
);
129101
}}
130102
className="w-16 px-2 py-1 text-center border border-gray-300 rounded mr-2"
131103
/>
132104
<Button
133-
handleButtonClick={() =>
134-
handleRemoveProductClick(
135-
item.key,
136-
data.cart.contents.nodes,
137-
)
138-
}
105+
handleButtonClick={() => handleRemoveProductClick(item.cartKey)}
139106
variant="secondary"
140-
buttonDisabled={updateCartProcessing}
141107
>
142108
Fjern
143109
</Button>
144110
</div>
145111
<div className="ml-4">
146-
<p className="text-lg font-semibold">{item.subtotal}</p>
112+
<p className="text-lg font-semibold">{item.totalPrice}</p>
147113
</div>
148114
</div>
149115
))}
150116
</div>
151117
<div className="bg-white rounded-lg p-6 md:w-full">
152118
<div className="flex justify-end mb-4">
153119
<span className="font-semibold pr-2">Subtotal:</span>
154-
<span>{cartTotal}</span>
120+
<span>{cart.totalProductsPrice}</span>
155121
</div>
156122
{!isCheckoutPage && (
157123
<div className="flex justify-center mb-4">
@@ -172,14 +138,6 @@ const CartContents = () => {
172138
</Link>
173139
</div>
174140
)}
175-
{updateCartProcessing && (
176-
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50">
177-
<div className="bg-white p-4 rounded-lg">
178-
<p className="text-lg mb-2">Oppdaterer handlekurv...</p>
179-
<LoadingSpinner />
180-
</div>
181-
</div>
182-
)}
183141
</div>
184142
);
185143
};

‎src/components/Checkout/CheckoutForm.component.tsx‎

Lines changed: 10 additions & 31 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,{RootObject } from '@/stores/cart';
1515

1616
// Utils
1717
import {
@@ -51,29 +51,17 @@ 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);
5858

5959
// Get cart data query
60-
const{ data, refetch }=useQuery(GET_CART, {
60+
useQuery(GET_CART, {
6161
notifyOnNetworkStatusChange: true,
62-
onCompleted: () => {
63-
// Update cart in the localStorage.
64-
const updatedCart = getFormattedCart(data);
65-
66-
if (!updatedCart && !data.cart.contents.nodes.length) {
67-
localStorage.removeItem('woo-session');
68-
localStorage.removeItem('wooocommerce-cart');
69-
setCart(null);
70-
return;
71-
}
72-
73-
localStorage.setItem('woocommerce-cart', JSON.stringify(updatedCart));
74-
75-
// Update cart data in React Context.
76-
setCart(updatedCart);
62+
onCompleted: (data) => {
63+
const updatedCart = getFormattedCart(data) as RootObject | undefined;
64+
setCart(updatedCart || null);
7765
},
7866
});
7967

@@ -84,16 +72,14 @@ const CheckoutForm = () => {
8472
variables: {
8573
input: orderData,
8674
},
75+
refetchQueries: [{ query: GET_CART }],
76+
awaitRefetchQueries: true,
8777
onCompleted: () => {
88-
localStorage.removeItem('woo-session');
89-
localStorage.removeItem('wooocommerce-cart');
9078
setorderCompleted(true);
9179
setCart(null);
92-
refetch();
9380
},
9481
onError: (error) => {
9582
setRequestError(error);
96-
refetch();
9783
},
9884
},
9985
);
@@ -102,15 +88,8 @@ const CheckoutForm = () => {
10288
if (null !== orderData) {
10389
// Perform checkout mutation when the value for orderData changes.
10490
checkout();
105-
setTimeout(() => {
106-
refetch();
107-
}, 2000);
10891
}
109-
}, [checkout, orderData, refetch]);
110-
111-
useEffect(() => {
112-
refetch();
113-
}, [refetch]);
92+
}, [checkout, orderData]);
11493

11594
const handleFormSubmit = (submitData: ICheckoutDataProps) => {
11695
const checkOutData = createCheckoutData(submitData);

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /