woocommerce/plugins/woocommerce-blocks/assets/js/base/context/hooks/use-checkout-address.js

131 lines
3.7 KiB
JavaScript

/**
* External dependencies
*/
import { defaultAddressFields } from '@woocommerce/settings';
import { useState, useEffect, useCallback, useRef } from '@wordpress/element';
/**
* Internal dependencies
*/
import {
useShippingDataContext,
useCustomerDataContext,
useCheckoutContext,
} from '../providers/cart-checkout';
/**
* Compare two addresses and see if they are the same.
*
* @param {Object} address1 First address.
* @param {Object} address2 Second address.
*/
const isSameAddress = ( address1, address2 ) => {
return Object.keys( defaultAddressFields ).every(
( field ) => address1[ field ] === address2[ field ]
);
};
/**
* Custom hook for exposing address related functionality for the checkout address form.
*/
export const useCheckoutAddress = () => {
const { customerId } = useCheckoutContext();
const { needsShipping } = useShippingDataContext();
const {
billingData,
setBillingData,
shippingAddress,
setShippingAddress,
} = useCustomerDataContext();
// This tracks the state of the "shipping as billing" address checkbox. It's
// initial value is true (if shipping is needed), however, if the user is
// logged in and they have a different billing address, we can toggle this off.
const [ shippingAsBilling, setShippingAsBilling ] = useState(
() =>
needsShipping &&
( ! customerId || isSameAddress( shippingAddress, billingData ) )
);
const currentShippingAsBilling = useRef( shippingAsBilling );
const previousBillingData = useRef( billingData );
/**
* Sets shipping address data, and also billing if using the same address.
*/
const setShippingFields = useCallback(
( value ) => {
setShippingAddress( value );
if ( shippingAsBilling ) {
setBillingData( value );
}
},
[ shippingAsBilling, setShippingAddress, setBillingData ]
);
/**
* Sets billing address data, and also shipping if shipping is disabled.
*/
const setBillingFields = useCallback(
( value ) => {
setBillingData( value );
if ( ! needsShipping ) {
setShippingAddress( value );
}
},
[ needsShipping, setShippingAddress, setBillingData ]
);
// When the "Use same address" checkbox is toggled we need to update the current billing address to reflect this;
// that is either setting the billing address to the shipping address, or restoring the billing address to it's
// previous state.
useEffect( () => {
if ( currentShippingAsBilling.current !== shippingAsBilling ) {
if ( shippingAsBilling ) {
previousBillingData.current = billingData;
setBillingData( shippingAddress );
} else {
const {
// We need to pluck out email and phone from previous billing data because they can be empty, causing the current email and phone to get emptied. See issue #4155
/* eslint-disable no-unused-vars */
email,
phone,
/* eslint-enable no-unused-vars */
...billingAddress
} = previousBillingData.current;
setBillingData( {
...billingAddress,
} );
}
currentShippingAsBilling.current = shippingAsBilling;
}
}, [ shippingAsBilling, setBillingData, shippingAddress, billingData ] );
const setEmail = ( value ) =>
void setBillingData( {
email: value,
} );
const setPhone = ( value ) =>
void setBillingData( {
phone: value,
} );
// Note that currentShippingAsBilling is returned rather than the current state of shippingAsBilling--this is so that
// the billing fields are not rendered before sync (billing field values are debounced and would be outdated)
return {
defaultAddressFields,
shippingFields: shippingAddress,
setShippingFields,
billingFields: billingData,
setBillingFields,
setEmail,
setPhone,
shippingAsBilling,
setShippingAsBilling,
showBillingFields:
! needsShipping || ! currentShippingAsBilling.current,
};
};