ItemQuantity
Description
Provides quantity management for cart items with automatic cart updates, debouncing, min/max validation, and loading states. Available as both a hook and render props component.
Import
import { ItemQuantity, useItemQuantity } from '@components/frontStore/cart/ItemQuantity';
Usage
As Render Props Component
import { ItemQuantity } from '@components/frontStore/cart/ItemQuantity';
function CartItem({ item }) {
return (
<ItemQuantity cartItemId={item.cartItemId} initialValue={item.qty}>
{({ quantity, increase, decrease, loading }) => (
<div>
<button onClick={decrease} disabled={loading}>-</button>
<span>{quantity}</span>
<button onClick={increase} disabled={loading}>+</button>
</div>
)}
</ItemQuantity>
);
}
As Hook
import { useItemQuantity } from '@components/frontStore/cart/ItemQuantity';
function CartItem({ item }) {
const { quantity, increase, decrease, inputProps } = useItemQuantity({
cartItemId: item.cartItemId,
initialValue: item.qty,
min: 1,
max: 10
});
return (
<div>
<button onClick={decrease}>-</button>
<input {...inputProps} />
<button onClick={increase}>+</button>
</div>
);
}
Props
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
| cartItemId | string | Yes | - | Cart item ID to update |
| initialValue | number | No | 1 | Initial quantity value |
| min | number | No | 1 | Minimum quantity allowed |
| max | number | No | Infinity | Maximum quantity allowed |
| debounce | number | No | 500 | Debounce delay in ms |
| onChange | (qty: number) => void | No | - | Called when quantity changes |
| onSuccess | () => void | No | - | Called on successful update |
| onFailure | (error: Error) => void | No | - | Called on update failure |
| children | RenderFunction | Yes* | - | Render function (component only) |
Return Values
| Name | Type | Description |
|---|---|---|
| quantity | number | Current quantity value |
| loading | boolean | True when cart is updating |
| increase | () => void | Increment quantity by 1 |
| decrease | () => void | Decrement quantity by 1 |
| setQuantity | (qty: number) => void | Set specific quantity |
| inputProps | object | Props to spread on input element |
Examples
Basic Quantity Selector
import { ItemQuantity } from '@components/frontStore/cart/ItemQuantity';
function QuantitySelector({ item }) {
return (
<ItemQuantity cartItemId={item.cartItemId} initialValue={item.qty}>
{({ quantity, increase, decrease, loading }) => (
<div className="quantity-selector">
<button
onClick={decrease}
disabled={loading || quantity <= 1}
>
-
</button>
<span>{quantity}</span>
<button onClick={increase} disabled={loading}>
+
</button>
</div>
)}
</ItemQuantity>
);
}
With Input Field
import { useItemQuantity } from '@components/frontStore/cart/ItemQuantity';
function QuantityInput({ item }) {
const { inputProps, increase, decrease, loading } = useItemQuantity({
cartItemId: item.cartItemId,
initialValue: item.qty,
min: 1,
max: 99
});
return (
<div className="flex gap-2">
<button onClick={decrease} disabled={loading}>-</button>
<input {...inputProps} className="w-16 text-center" />
<button onClick={increase} disabled={loading}>+</button>
</div>
);
}
With Stock Limit
import { ItemQuantity } from '@components/frontStore/cart/ItemQuantity';
function CartItemRow({ item }) {
return (
<ItemQuantity
cartItemId={item.cartItemId}
initialValue={item.qty}
max={item.product.stockQuantity}
>
{({ quantity, increase, decrease, loading }) => (
<div>
<span>{item.productName}</span>
<div className="quantity-controls">
<button onClick={decrease} disabled={loading}>-</button>
<span>{quantity}</span>
<button
onClick={increase}
disabled={loading || quantity >= item.product.stockQuantity}
>
+
</button>
</div>
<span>In stock: {item.product.stockQuantity}</span>
</div>
)}
</ItemQuantity>
);
}
With Callbacks
import { useItemQuantity } from '@components/frontStore/cart/ItemQuantity';
import { useState } from 'react';
function CartItem({ item }) {
const [message, setMessage] = useState('');
const { quantity, increase, decrease, loading } = useItemQuantity({
cartItemId: item.cartItemId,
initialValue: item.qty,
debounce: 300,
onChange: (qty) => {
console.log('Quantity changed to:', qty);
},
onSuccess: () => {
setMessage('Updated successfully');
setTimeout(() => setMessage(''), 2000);
},
onFailure: (error) => {
setMessage(`Error: ${error.message}`);
}
});
return (
<div>
<button onClick={decrease}>-</button>
<span>{quantity}</span>
<button onClick={increase}>+</button>
{loading && <span>Updating...</span>}
{message && <div>{message}</div>}
</div>
);
}
Complete Cart Item
import { ItemQuantity } from '@components/frontStore/cart/ItemQuantity';
import { Image } from '@components/common/Image';
function CartItem({ item, onRemove }) {
return (
<div className="cart-item">
<Image src={item.thumbnail} alt={item.productName} width={80} height={80} />
<div className="item-details">
<h3>{item.productName}</h3>
<p>{item.productPrice.text}</p>
</div>
<ItemQuantity
cartItemId={item.cartItemId}
initialValue={item.qty}
min={1}
max={10}
debounce={500}
>
{({ quantity, increase, decrease, loading, inputProps }) => (
<div className="quantity-wrapper">
<label>Quantity:</label>
<div className="controls">
<button
onClick={decrease}
disabled={loading || quantity <= 1}
className="btn-decrease"
>
-
</button>
<input
{...inputProps}
className="quantity-input"
/>
<button
onClick={increase}
disabled={loading || quantity >= 10}
className="btn-increase"
>
+
</button>
</div>
{loading && <span className="loading">Updating...</span>}
</div>
)}
</ItemQuantity>
<button onClick={() => onRemove(item.cartItemId)}>Remove</button>
</div>
);
}
Features
- Debounced Updates: Waits 500ms before updating cart (configurable)
- Min/Max Validation: Enforces quantity limits
- Loading State: Disables controls during updates
- Error Recovery: Reverts to previous value on failure
- Input Integration: Pre-configured input props
- Callbacks: Success/failure/change handlers
- Optimistic UI: Updates immediately, syncs with server
- Smart Diff: Only sends changed quantity to server
Behavior
Debouncing
Updates are debounced by default (500ms). Set debounce={0} for immediate updates. The decrease and increase functions use debouncing, but blur events trigger immediate updates.
Validation
Quantity is clamped between min and max values. Invalid input is ignored, empty input becomes 0 and is clamped on blur.
Cart Updates
Uses cartDispatch.updateItem with action: 'increase' or action: 'decrease' and the quantity difference. On failure, reverts to previous quantity.
Related Components
- CartItems - Cart display component
- CartContext - Cart state management
- AddToCart - Add to cart component