Nghiên cứu từng bước
Example 1
lib\queries.ts
query GetProducts {
products(first: 10) {
nodes {
id
databaseId
name
slug
type
... on SimpleProduct {
price
regularPrice
onSale
}
... on VariableProduct {
price
regularPrice
variations {
nodes {
id
databaseId
name
price
regularPrice
attributes {
nodes {
name
value
}
}
}
}
}
... on ExternalProduct {
price
regularPrice
}
... on GroupProduct {
price
}
}
}
}
Kết quả
{
"data": {
"products": {
"nodes": [
{
"id": "cHJvZHVjdDoxNzk2",
"databaseId": 1796,
"name": "WordPress Pennant",
"slug": "wordpress-pennant",
"type": "EXTERNAL",
"price": "11 ₫",
"regularPrice": "11 ₫"
},
{
"id": "cHJvZHVjdDoxNzk1",
"databaseId": 1795,
"name": "Logo Collection",
"slug": "logo-collection",
"type": "GROUPED",
"price": "18 ₫ - 45 ₫"
},
{
"id": "cHJvZHVjdDoxNzk0",
"databaseId": 1794,
"name": "Beanie with Logo",
"slug": "beanie-with-logo",
"type": "SIMPLE",
"price": "18 ₫",
"regularPrice": "20 ₫",
"onSale": true
},
{
"id": "cHJvZHVjdDoxNzkz",
"databaseId": 1793,
"name": "T-Shirt with Logo",
"slug": "t-shirt-with-logo",
"type": "SIMPLE",
"price": "18 ₫",
"regularPrice": "18 ₫",
"onSale": false
},
{
"id": "cHJvZHVjdDoxNzg2",
"databaseId": 1786,
"name": "Single",
"slug": "single",
"type": "SIMPLE",
"price": "2 ₫",
"regularPrice": "3 ₫",
"onSale": true
},
{
"id": "cHJvZHVjdDoxNzg1",
"databaseId": 1785,
"name": "Album",
"slug": "album",
"type": "SIMPLE",
"price": "15 ₫",
"regularPrice": "15 ₫",
"onSale": false
},
{
"id": "cHJvZHVjdDoxNzg0",
"databaseId": 1784,
"name": "Polo",
"slug": "polo",
"type": "SIMPLE",
"price": "20 ₫",
"regularPrice": "20 ₫",
"onSale": false
},
{
"id": "cHJvZHVjdDoxNzgz",
"databaseId": 1783,
"name": "Long Sleeve Tee",
"slug": "long-sleeve-tee",
"type": "SIMPLE",
"price": "25 ₫",
"regularPrice": "25 ₫",
"onSale": false
},
{
"id": "cHJvZHVjdDoxNzgy",
"databaseId": 1782,
"name": "Hoodie with Zipper",
"slug": "hoodie-with-zipper",
"type": "SIMPLE",
"price": "45 ₫",
"regularPrice": "45 ₫",
"onSale": false
},
{
"id": "cHJvZHVjdDoxNzgx",
"databaseId": 1781,
"name": "Hoodie with Pocket",
"slug": "hoodie-with-pocket",
"type": "SIMPLE",
"price": "35 ₫",
"regularPrice": "45 ₫",
"onSale": true
},
{
"id": "cHJvZHVjdDoxNzgw",
"databaseId": 1780,
"name": "Sunglasses",
"slug": "sunglasses",
"type": "SIMPLE",
"price": "90 ₫",
"regularPrice": "90 ₫",
"onSale": false
},
{
"id": "cHJvZHVjdDoxNzc5",
"databaseId": 1779,
"name": "Cap",
"slug": "cap",
"type": "SIMPLE",
"price": "16 ₫",
"regularPrice": "18 ₫",
"onSale": true
},
{
"id": "cHJvZHVjdDoxNzc4",
"databaseId": 1778,
"name": "Belt",
"slug": "belt",
"type": "SIMPLE",
"price": "55 ₫",
"regularPrice": "65 ₫",
"onSale": true
},
{
"id": "cHJvZHVjdDoxNzc3",
"databaseId": 1777,
"name": "Beanie",
"slug": "beanie",
"type": "SIMPLE",
"price": "18 ₫",
"regularPrice": "20 ₫",
"onSale": true
},
{
"id": "cHJvZHVjdDoxNzc2",
"databaseId": 1776,
"name": "T-Shirt",
"slug": "t-shirt",
"type": "SIMPLE",
"price": "18 ₫",
"regularPrice": "18 ₫",
"onSale": false
},
{
"id": "cHJvZHVjdDoxNzc1",
"databaseId": 1775,
"name": "Hoodie with Logo",
"slug": "hoodie-with-logo",
"type": "SIMPLE",
"price": "45 ₫",
"regularPrice": "45 ₫",
"onSale": false
},
{
"id": "cHJvZHVjdDoxNzc0",
"databaseId": 1774,
"name": "Hoodie",
"slug": "hoodie",
"type": "VARIABLE",
"price": "42 ₫ - 65 ₫",
"regularPrice": "50 ₫ - 65 ₫",
"variations": {
"nodes": [
{
"id": "cHJvZHVjdF92YXJpYXRpb246MTc5MA==",
"databaseId": 1790,
"name": "Hoodie - Red, Medium",
"price": "42 ₫",
"regularPrice": "50 ₫",
"attributes": {
"nodes": [
{
"name": "pa_color",
"value": "Red"
},
{
"name": "pa_size",
"value": "Medium"
}
]
}
},
{
"id": "cHJvZHVjdF92YXJpYXRpb246MTc5MQ==",
"databaseId": 1791,
"name": "Hoodie - Green, Small",
"price": "55 ₫",
"regularPrice": "55 ₫",
"attributes": {
"nodes": [
{
"name": "pa_color",
"value": "Green"
},
{
"name": "pa_size",
"value": "Small"
}
]
}
},
{
"id": "cHJvZHVjdF92YXJpYXRpb246MTc5Mg==",
"databaseId": 1792,
"name": "Hoodie - Green, Medium",
"price": "65 ₫",
"regularPrice": "65 ₫",
"attributes": {
"nodes": [
{
"name": "pa_color",
"value": "Green"
},
{
"name": "pa_size",
"value": "Medium"
}
]
}
},
{
"id": "cHJvZHVjdF92YXJpYXRpb246MTc5Nw==",
"databaseId": 1797,
"name": "Hoodie - Pink, Large",
"price": "60 ₫",
"regularPrice": "60 ₫",
"attributes": {
"nodes": [
{
"name": "pa_color",
"value": "Pink"
},
{
"name": "pa_size",
"value": "Large"
}
]
}
}
]
}
},
{
"id": "cHJvZHVjdDoxNzcz",
"databaseId": 1773,
"name": "V-Neck T-Shirt",
"slug": "v-neck-t-shirt",
"type": "VARIABLE",
"price": "15 ₫ - 20 ₫",
"regularPrice": "15 ₫ - 20 ₫",
"variations": {
"nodes": [
{
"id": "cHJvZHVjdF92YXJpYXRpb246MTc4Nw==",
"databaseId": 1787,
"name": "V-Neck T-Shirt - Green, Medium",
"price": "20 ₫",
"regularPrice": "20 ₫",
"attributes": {
"nodes": [
{
"name": "pa_color",
"value": "Green"
},
{
"name": "pa_size",
"value": "Medium"
}
]
}
},
{
"id": "cHJvZHVjdF92YXJpYXRpb246MTc4OA==",
"databaseId": 1788,
"name": "V-Neck T-Shirt - Green, Large",
"price": "20 ₫",
"regularPrice": "20 ₫",
"attributes": {
"nodes": [
{
"name": "pa_color",
"value": "Green"
},
{
"name": "pa_size",
"value": "Large"
}
]
}
},
{
"id": "cHJvZHVjdF92YXJpYXRpb246MTc4OQ==",
"databaseId": 1789,
"name": "V-Neck T-Shirt - Gray, Large",
"price": "15 ₫",
"regularPrice": "15 ₫",
"attributes": {
"nodes": [
{
"name": "pa_color",
"value": "Gray"
},
{
"name": "pa_size",
"value": "Large"
}
]
}
}
]
}
}
]
}
}
}
Add id 1775 SINGLE
mutation AddToCart {
addToCart(input: {productId: 1775}) {
cart {
contents {
nodes {
product {
node {
name
databaseId
... on SimpleProduct {
price
}
... on VariableProduct {
price
}
}
}
quantity
}
}
subtotal
total
}
}
}
Kết quả:
{
"data": {
"addToCart": {
"cart": {
"contents": {
"nodes": [
{
"product": {
"node": {
"name": "Hoodie with Logo",
"databaseId": 1775,
"price": "45 ₫"
}
},
"quantity": 1
}
]
},
"subtotal": "45 ₫",
"total": "45 ₫"
}
}
},
"extensions": {
"debug": [],
"queryAnalyzer": {
"keys": "569f2fdc8514dd4007f60ff2235b1904c3d9c3bf5722bf49ae373764258462d9 graphql:Query operation:AddToCart list:variableproduct list:externalproduct list:groupproduct list:simpleproduct",
"keysLength": 177,
"keysCount": 7,
"skippedKeys": "",
"skippedKeysSize": 0,
"skippedKeysCount": 0,
"skippedTypes": []
}
}
}
Add 1790 variations
mutation AddToCart {
addToCart(input: {productId: 1790}) {
cart {
contents {
nodes {
product {
node {
name
databaseId
... on SimpleProduct {
price
}
... on VariableProduct {
price
}
}
}
quantity
}
}
subtotal
total
}
}
}
{
"data": {
"addToCart": {
"cart": {
"contents": {
"nodes": [
{
"product": {
"node": {
"name": "Hoodie",
"databaseId": 1774,
"price": "42 ₫ - 65 ₫"
}
},
"quantity": 1
}
]
},
"subtotal": "42 ₫",
"total": "42 ₫"
}
}
}
}
Add 1791 variations
mutation AddToCart {
addToCart(input: {productId: 1791}) {
cart {
contents {
nodes {
product {
node {
name
databaseId
... on SimpleProduct {
price
}
... on VariableProduct {
price
}
}
}
quantity
}
}
subtotal
total
}
}
}
Kết quả:
{
"data": {
"addToCart": {
"cart": {
"contents": {
"nodes": [
{
"product": {
"node": {
"name": "Hoodie",
"databaseId": 1774,
"price": "42 ₫ - 65 ₫"
}
},
"quantity": 1
}
]
},
"subtotal": "55 ₫",
"total": "55 ₫"
}
}
}
}
Cách thêm có biến thể ghi đầy đủ ProductId && variationId
mutation AddToCart {
addToCart(input: {productId: 1774, quantity: 3, variationId: 1791}) {
cart {
contents {
nodes {
quantity
variation {
node {
databaseId
name
price
}
}
}
}
subtotal
total
}
}
}
Cách thêm trực tiếp này cũng ổn
mutation AddToCart {
addToCart(input: {productId: 1791, quantity: 3}) {
cart {
contents {
nodes {
quantity
variation {
node {
databaseId
name
price
}
}
}
}
subtotal
total
}
}
}
Get Cart
query getCart {
cart {
total
totalTax
subtotalTax
subtotal
contents {
nodes {
product {
node {
id
image {
sourceUrl
}
}
}
quantity
subtotal
subtotalTax
tax
total
variation {
node {
price
salePrice
regularPrice
}
}
}
}
}
}
{
"data": {
"cart": {
"total": "2.277 ₫",
"totalTax": "0 ₫",
"subtotalTax": "0 ₫",
"subtotal": "2.277 ₫",
"contents": {
"nodes": [
{
"product": {
"node": {
"id": "cHJvZHVjdDoxNzc0",
"image": {
"sourceUrl": "https://wp1.com/wp-content/uploads/2025/02/hoodie-2.jpg"
}
}
},
"quantity": 11,
"subtotal": "462 ₫",
"subtotalTax": "0 ₫",
"tax": "0 ₫",
"total": "462 ₫",
"variation": {
"node": {
"price": "42 ₫",
"salePrice": "42 ₫",
"regularPrice": "50 ₫"
}
}
},
{
"product": {
"node": {
"id": "cHJvZHVjdDoxNzc0",
"image": {
"sourceUrl": "https://wp1.com/wp-content/uploads/2025/02/hoodie-2.jpg"
}
}
},
"quantity": 33,
"subtotal": "1.815 ₫",
"subtotalTax": "0 ₫",
"tax": "0 ₫",
"total": "1.815 ₫",
"variation": {
"node": {
"price": "55 ₫",
"salePrice": null,
"regularPrice": "55 ₫"
}
}
}
]
}
}
}
}
Chú ý: Khi chưa sử dụng đăng nhập, mặc dù thêm đươc vào cart nhưng nó sẽ không lấy được sản phẩm mới thêm vào.
Hủy giỏ hàng tức đưa rỏ hàng về rỗng
mutation removeItemsFromCart {
removeItemsFromCart(input: {all: true}) {
cart {
isEmpty
}
}
}
Xóa bỏ một sản phẩm nào đó
Trước tiên ta phải lấy được key
query getCart {
cart {
total
totalTax
subtotalTax
subtotal
contents {
nodes {
key
product {
node {
id
image {
sourceUrl
}
}
}
quantity
subtotal
subtotalTax
tax
total
variation {
node {
price
salePrice
regularPrice
}
}
}
}
}
}
{
"data": {
"cart": {
"total": "2.277 ₫",
"totalTax": "0 ₫",
"subtotalTax": "0 ₫",
"subtotal": "2.277 ₫",
"contents": {
"nodes": [
{
"key": "19f6eb66e580666ea01a86a19bad0822",
"product": {
"node": {
"id": "cHJvZHVjdDoxNzc0",
"image": {
"sourceUrl": "https://wp1.com/wp-content/uploads/2025/02/hoodie-2.jpg"
}
}
},
"quantity": 11,
"subtotal": "462 ₫",
"subtotalTax": "0 ₫",
"tax": "0 ₫",
"total": "462 ₫",
"variation": {
"node": {
"price": "42 ₫",
"salePrice": "42 ₫",
"regularPrice": "50 ₫"
}
}
},
{
"key": "d861b07ce21927000014149b8cc0556b",
"product": {
"node": {
"id": "cHJvZHVjdDoxNzc0",
"image": {
"sourceUrl": "https://wp1.com/wp-content/uploads/2025/02/hoodie-2.jpg"
}
}
},
"quantity": 33,
"subtotal": "1.815 ₫",
"subtotalTax": "0 ₫",
"tax": "0 ₫",
"total": "1.815 ₫",
"variation": {
"node": {
"price": "55 ₫",
"salePrice": null,
"regularPrice": "55 ₫"
}
}
}
]
}
}
}
}
Vi dụ tôi muốn xóa bỏ key 19f6eb66e580666ea01a86a19bad0822
mutation selelectItemsRemove {
removeItemsFromCart(input: {keys: "19f6eb66e580666ea01a86a19bad0822"}) {
cartItems {
id
variation {
node {
databaseId
}
}
}
}
}
query getCart {
cart {
total
totalTax
subtotalTax
subtotal
contents {
nodes {
key
product {
node {
id
image {
sourceUrl
}
}
}
quantity
subtotal
subtotalTax
tax
total
variation {
node {
price
salePrice
regularPrice
}
}
}
}
}
}
Kết quả còn lại
{
"data": {
"cart": {
"total": "1.815 ₫",
"totalTax": "0 ₫",
"subtotalTax": "0 ₫",
"subtotal": "1.815 ₫",
"contents": {
"nodes": [
{
"key": "d861b07ce21927000014149b8cc0556b",
"product": {
"node": {
"id": "cHJvZHVjdDoxNzc0",
"image": {
"sourceUrl": "https://wp1.com/wp-content/uploads/2025/02/hoodie-2.jpg"
}
}
},
"quantity": 33,
"subtotal": "1.815 ₫",
"subtotalTax": "0 ₫",
"tax": "0 ₫",
"total": "1.815 ₫",
"variation": {
"node": {
"price": "55 ₫",
"salePrice": null,
"regularPrice": "55 ₫"
}
}
}
]
}
}
}
}
Khởi tại thêm các sản phẩm trong cart để thực hiện xóa
{
"data": {
"cart": {
"total": "2.106 ₫",
"totalTax": "0 ₫",
"subtotalTax": "0 ₫",
"subtotal": "2.106 ₫",
"contents": {
"nodes": [
{
"key": "d861b07ce21927000014149b8cc0556b",
"product": {
"node": {
"id": "cHJvZHVjdDoxNzc0",
"image": {
"sourceUrl": "https://wp1.com/wp-content/uploads/2025/02/hoodie-2.jpg"
}
}
},
"quantity": 36,
"subtotal": "1.980 ₫",
"subtotalTax": "0 ₫",
"tax": "0 ₫",
"total": "1.980 ₫",
"variation": {
"node": {
"price": "55 ₫",
"salePrice": null,
"regularPrice": "55 ₫"
}
}
},
{
"key": "19f6eb66e580666ea01a86a19bad0822",
"product": {
"node": {
"id": "cHJvZHVjdDoxNzc0",
"image": {
"sourceUrl": "https://wp1.com/wp-content/uploads/2025/02/hoodie-2.jpg"
}
}
},
"quantity": 3,
"subtotal": "126 ₫",
"subtotalTax": "0 ₫",
"tax": "0 ₫",
"total": "126 ₫",
"variation": {
"node": {
"price": "42 ₫",
"salePrice": "42 ₫",
"regularPrice": "50 ₫"
}
}
}
]
}
}
}
}
— Giờ muốn xáo key 19f6eb66e580666ea01a86a19bad0822
mutation selelectItemsRemove {
removeItemsFromCart(input: {keys: "19f6eb66e580666ea01a86a19bad0822"}) {
cartItems {
variation {
node {
databaseId
}
}
}
cart {
contents {
nodes {
quantity
subtotal
subtotalTax
tax
total
variation {
node {
databaseId
id
name
}
}
}
}
}
}
}
Kết quả đúng mong đợi
Example 2 (Dựa vào lý thuyết trên làm dự án thực tế)
.env
NEXT_PUBLIC_BACKEND_URL=http://wp1.com/graphql
WORDPRESS_REST_API_URL=http://wp1.com/wp-json
NEXT_PUBLIC_ORIGIN_URL=http://wp1.com
NEXTAUTH_SECRET=jg3I65KxxWvmLWwbI8Zp9DbJsgyVJ+vHRAARmhF68+A=
NODE_TLS_REJECT_UNAUTHORIZED=0
NEXT_PUBLIC_WORDPRESS_GRAPHQL_ENDPOINT=http://wp1.com/graphql
pages\products.tsx
// pages/products.tsx
import { useQuery, useMutation } from '@apollo/client';
import client from '@/lib/apolloClient';
import { GET_PRODUCTS, ADD_TO_CART } from '@/lib/queries';
import { useState } from 'react';
import { ProductsQueryData, AddToCartMutationData, Product, CartItem, Variation, } from '@/lib/types';
import { useCart } from '@/lib/CartContext';
import Cart from '@/components/Cart';
export default function Products() {
const { loading, error, data } = useQuery<ProductsQueryData>(GET_PRODUCTS, {
client,
});
const [addToCartMutation] = useMutation<AddToCartMutationData>(ADD_TO_CART, {
client,
});
const { addToCart } = useCart();
if (loading) return <p>Đang tải...</p>;
if (error) return <p>Lỗi: {error.message}</p>;
const products: Product[] = data?.products.nodes || [];
const handleAddToCart = async (product: Product, variation?: Variation) => {
try {
const input = {
productId: product.databaseId,
quantity: 1,
variationId: variation?.databaseId || null,
clientMutationId: 'unique-id',
};
const { data } = await addToCartMutation({ variables: { input } });
addToCart(product, variation); // Thêm vào giỏ hàng cục bộ
alert('Đã thêm vào giỏ hàng!');
} catch (err) {
console.error('Lỗi khi thêm vào giỏ hàng:', err);
}
};
return (
<div className='flex'>
<h1>Sản phẩm</h1>
<ul>
{products.map((product) => (
<li key={product.id}>
<h2>{product.name}</h2>
<p>Giá: {product.price || 'Không xác định'}</p>
<p>Giá gốc: {product.regularPrice || 'Không xác định'}</p>
{product.type === 'SIMPLE' && (
<button onClick={() => handleAddToCart(product)} className='rounded-full bg-sky-500 px-5 py-2 text-sm leading-5 font-semibold text-white hover:bg-sky-700'>
Thêm vào giỏ hàng
</button>
)}
{product.type === 'VARIABLE' && product.variations && (
<ul>
{product.variations.nodes.map((variation) => (
<li key={variation.id}>
{variation.name} - Giá: {variation.price || 'Không xác định'}
<button
onClick={() =>
handleAddToCart(product, variation)
}
className='rounded-full bg-pink-500 px-5 py-2 text-sm leading-5 font-semibold text-white hover:bg-pink-700'
>
Thêm vào giỏ hàng
</button>
</li>
))}
</ul>
)}
</li>
))}
</ul>
<Cart />
</div>
);
}
pages\_app.tsx
import { ApolloProvider } from '@apollo/client';
import client from '@/lib/apolloClient';
import { CartProvider } from '@/lib/CartContext';
import '@/styles/globals.css';
import type { AppProps } from 'next/app';
export default function MyApp({ Component, pageProps }: AppProps) {
return (
<ApolloProvider client={client}>
<CartProvider>
<Component {...pageProps} />
</CartProvider>
</ApolloProvider>
);
}
lib\types.ts
export interface ProductAttribute {
name: string;
value: string;
}
export interface Variation {
id: string;
databaseId: number;
name: string;
price: string | null;
regularPrice: string | null;
attributes: {
nodes: ProductAttribute[];
};
}
export interface Product {
id: string;
databaseId: number;
name: string;
slug: string;
type: 'SIMPLE' | 'VARIABLE' | 'EXTERNAL' | 'GROUP';
price: string | null;
regularPrice: string | null;
onSale?: boolean; // Chỉ có trong SimpleProduct
variations?: {
nodes: Variation[];
};
}
export interface CartItem {
product: Product;
variation?: Variation;
quantity: number;
}
export interface ProductsQueryData {
products: {
nodes: Product[];
};
}
export interface AddToCartMutationData {
addToCart: {
cart: {
product: Product
variation?: Variation;
quantity: number;
}[];
subtotal: string;
total: string;
};
}
lib\queries.ts
import { gql } from '@apollo/client';
export const GET_PRODUCTS = gql`
query GetProducts {
products(first: 10) {
nodes {
id
databaseId
name
slug
type
... on SimpleProduct {
price
regularPrice
onSale
}
... on VariableProduct {
price
regularPrice
variations {
nodes {
id
databaseId
name
price
regularPrice
attributes {
nodes {
name
value
}
}
}
}
}
... on ExternalProduct {
price
regularPrice
}
... on GroupProduct {
price
}
}
}
}
`;
export const ADD_TO_CART = gql`
mutation AddToCart($input: AddToCartInput!) {
addToCart(input: $input) {
cart {
contents {
nodes {
product {
node {
name
databaseId
... on SimpleProduct {
price
}
... on VariableProduct {
price
}
}
}
quantity
}
}
subtotal
total
}
}
}
`;
lib\CartContext.tsx
// lib/CartContext.tsx
import { createContext, useContext, useState, ReactNode } from 'react';
import { CartItem, Product, Variation } from './types';
interface CartContextType {
cart: CartItem[];
addToCart: (product: Product, variation?: Variation) => void;
removeFromCart: (productId: number, variationId?: number) => void;
updateQuantity: (productId: number, variationId: number | null, quantity: number) => void;
getTotal: () => string;
}
const CartContext = createContext<CartContextType | undefined>(undefined);
export const CartProvider = ({ children }: { children: ReactNode }) => {
const [cart, setCart] = useState<CartItem[]>([]);
const addToCart = (product: Product, variation?: Variation) => {
setCart((prevCart) => {
const existingItem = prevCart.find(
(item) =>
item.product.databaseId === product.databaseId &&
(!variation || item.variation?.databaseId === variation.databaseId)
);
if (existingItem) {
return prevCart.map((item) =>
item === existingItem ? { ...item, quantity: item.quantity + 1 } : item
);
}
return [...prevCart, { product, variation, quantity: 1 }];
});
};
const removeFromCart = (productId: number, variationId?: number) => {
setCart((prevCart) =>
prevCart.filter(
(item) =>
item.product.databaseId !== productId ||
(variationId && item.variation?.databaseId !== variationId)
)
);
};
const updateQuantity = (productId: number, variationId: number | null, quantity: number) => {
if (quantity < 1) {
removeFromCart(productId, variationId || undefined);
return;
}
setCart((prevCart) =>
prevCart.map((item) =>
item.product.databaseId === productId &&
(!variationId || item.variation?.databaseId === variationId)
? { ...item, quantity }
: item
)
);
};
const getTotal = () => {
const total = cart.reduce((sum, item) => {
const price = item.variation?.price || item.product.price || '0';
const priceValue = parseFloat(price.replace(/[^0-9.-]+/g, '')) || 0;
return sum + priceValue * item.quantity;
}, 0);
return total.toFixed(2);
};
return (
<CartContext.Provider value={{ cart, addToCart, removeFromCart, updateQuantity, getTotal }}>
{children}
</CartContext.Provider>
);
};
export const useCart = () => {
const context = useContext(CartContext);
if (!context) {
throw new Error('useCart must be used within a CartProvider');
}
return context;
};
lib\apolloClient.ts
// lib/apolloClient.ts
import { ApolloClient, InMemoryCache } from '@apollo/client';
const client = new ApolloClient({
uri: 'http://wp1.com/graphql', // Thay bằng URL GraphQL của bạn
cache: new InMemoryCache(),
});
export default client;
components\Cart.tsx
// components/Cart.tsx
import { useCart } from '@/lib/CartContext';
export default function Cart() {
const { cart, removeFromCart, updateQuantity, getTotal } = useCart();
return (
<div>
<h2>Giỏ hàng</h2>
{cart.length === 0 ? (
<p>Giỏ hàng trống</p>
) : (
<>
<ul>
{cart.map((item) => (
<li key={`${item.product.databaseId}-${item.variation?.databaseId || ''}`}>
<p>
{item.variation ? `${item.product.name} (${item.variation.name})` : item.product.name} -
Giá: {item.variation?.price || item.product.price || '0'} -
Số lượng: {item.quantity}
</p>
<button
onClick={() => updateQuantity(item.product.databaseId, item.variation?.databaseId || null, item.quantity + 1)}
>
+
</button>
<button
onClick={() => updateQuantity(item.product.databaseId, item.variation?.databaseId || null, item.quantity - 1)}
>
-
</button>
<button
onClick={() => removeFromCart(item.product.databaseId, item.variation?.databaseId)}
>
Xóa
</button>
</li>
))}
</ul>
<p>Tổng tiền: ${getTotal()}</p>
</>
)}
</div>
);
}
Example 3 hoàn thàn dự án shop, cart
PreviousTổng hợp các câu hỏi đã hỏiNextHướng dẫn đăng nhập wordpress bằng JWT Authentication for WP REST API (ok)
Last updated
Was this helpful?