woocommerce/plugins/woocommerce-blocks/assets/js/base/hooks/cart/use-store-cart-item-quantit...

118 lines
3.4 KiB
JavaScript

/**
* External dependencies
*/
import { useSelect, useDispatch } from '@wordpress/data';
import { useState, useEffect } from '@wordpress/element';
import { CART_STORE_KEY as storeKey } from '@woocommerce/block-data';
import { usePrevious } from '@woocommerce/base-hooks';
import { useDebounce } from 'use-debounce';
import { useCheckoutContext } from '@woocommerce/base-context';
import { triggerFragmentRefresh } from '@woocommerce/base-utils';
/**
* Internal dependencies
*/
import { useStoreCart } from './use-store-cart';
/**
* @typedef {import('@woocommerce/type-defs/hooks').StoreCartItemQuantity} StoreCartItemQuantity
* @typedef {import('@woocommerce/type-defs/cart').CartItem} CartItem
*/
/**
* This is a custom hook for loading the Store API /cart/ endpoint and
* actions for removing or changing item quantity.
*
* @see https://github.com/woocommerce/woocommerce-gutenberg-products-block/tree/main/src/RestApi/StoreApi
*
* @param {CartItem} cartItem The cartItem to get quantity info from and
* will have quantity updated on.
* @return {StoreCartItemQuantity} An object exposing data and actions relating
* to cart items.
*/
export const useStoreCartItemQuantity = ( cartItem ) => {
const { key: cartItemKey = '', quantity: cartItemQuantity = 1 } = cartItem;
const { cartErrors } = useStoreCart();
const { dispatchActions } = useCheckoutContext();
// Store quantity in hook state. This is used to keep the UI
// updated while server request is updated.
const [ quantity, changeQuantity ] = useState( cartItemQuantity );
const [ debouncedQuantity ] = useDebounce( quantity, 400 );
const previousDebouncedQuantity = usePrevious( debouncedQuantity );
const { removeItemFromCart, changeCartItemQuantity } = useDispatch(
storeKey
);
const isPendingQuantity = useSelect(
( select ) => {
if ( ! cartItemKey ) {
return false;
}
const store = select( storeKey );
return store.isItemPendingQuantity( cartItemKey );
},
[ cartItemKey ]
);
const isPendingDelete = useSelect(
( select ) => {
if ( ! cartItemKey ) {
return false;
}
const store = select( storeKey );
return store.isItemPendingDelete( cartItemKey );
},
[ cartItemKey ]
);
const removeItem = () => {
return cartItemKey
? removeItemFromCart( cartItemKey ).then( () => {
triggerFragmentRefresh();
} )
: false;
};
// Observe debounced quantity value, fire action to update server on change.
useEffect( () => {
// Don't run it if quantity didn't change but it was set for the first time.
if ( cartItemKey && Number.isFinite( previousDebouncedQuantity ) ) {
changeCartItemQuantity( cartItemKey, debouncedQuantity ).then(
triggerFragmentRefresh
);
}
}, [ debouncedQuantity, cartItemKey ] );
useEffect( () => {
if ( isPendingQuantity ) {
dispatchActions.incrementCalculating();
} else {
dispatchActions.decrementCalculating();
}
}, [ isPendingQuantity ] );
useEffect( () => {
if ( isPendingDelete ) {
dispatchActions.incrementCalculating();
} else if ( ! isPendingDelete && cartErrors.length ) {
dispatchActions.decrementCalculating();
}
return () => {
if ( isPendingDelete ) {
dispatchActions.decrementCalculating();
}
};
}, [ isPendingDelete ] );
return {
isPendingDelete,
quantity,
changeQuantity,
removeItem,
cartItemQuantityErrors: cartErrors,
};
};