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

120 lines
3.4 KiB
JavaScript

/**
* External dependencies
*/
import defaultAddressFields from '@woocommerce/base-components/cart-checkout/address-form/default-address-fields';
import { useState, useEffect } from '@wordpress/element';
import {
useShippingDataContext,
useBillingDataContext,
useCheckoutContext,
} from '@woocommerce/base-context';
import { isEqual } from 'lodash';
/**
* 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 tracking address field state on checkout and persisting it to
* context globally on change.
*/
export const useCheckoutAddress = () => {
const { customerId } = useCheckoutContext();
const {
shippingAddress,
setShippingAddress,
needsShipping,
} = useShippingDataContext();
const { billingData, setBillingData } = useBillingDataContext();
// These are the local states of address fields, which are persisted
// globally when changed. They default to the global shipping address which
// is populated from the current customer data or default location.
const [ shippingFields, updateShippingFields ] = useState(
shippingAddress
);
const [ billingFields, updateBillingFields ] = useState( billingData );
// 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 ) )
);
// Pushes to global state when changes are made locally.
useEffect( () => {
if ( ! isEqual( shippingFields, shippingAddress ) ) {
setShippingAddress( shippingFields );
}
// Uses shipping or billing fields depending on shippingAsBilling checkbox, but ensures
// billing only fields are also included.
const newBillingData = {
...( shippingAsBilling ? shippingFields : billingFields ),
email: billingFields.email,
phone: billingFields.phone,
};
if ( ! isEqual( newBillingData, billingData ) ) {
setBillingData( newBillingData );
}
}, [
shippingFields,
billingFields,
shippingAsBilling,
billingData,
shippingAddress,
setBillingData,
setShippingAddress,
] );
/**
* Wrapper for updateBillingFields (from useState) which handles merging.
*
* @param {Object} newValues New values to store to state.
*/
const setBillingFields = ( newValues ) =>
void updateBillingFields( ( prevState ) => ( {
...prevState,
...newValues,
} ) );
/**
* Wrapper for updateShippingFields (from useState) which handles merging.
*
* @param {Object} newValues New values to store to state.
*/
const setShippingFields = ( newValues ) =>
void updateShippingFields( ( prevState ) => ( {
...prevState,
...newValues,
} ) );
const setEmail = ( value ) => void setBillingFields( { email: value } );
const setPhone = ( value ) => void setBillingFields( { phone: value } );
return {
defaultAddressFields,
shippingFields,
setShippingFields,
billingFields,
setBillingFields,
setEmail,
setPhone,
shippingAsBilling,
setShippingAsBilling,
showBillingFields: ! needsShipping || ! shippingAsBilling,
};
};