Skip to content

Commit

Permalink
Shift side effects from checkout-state to checkout-processor
Browse files Browse the repository at this point in the history
  • Loading branch information
alexflorisca committed May 31, 2022
1 parent 4d42c46 commit 059533d
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 104 deletions.
109 changes: 103 additions & 6 deletions assets/js/base/context/providers/cart-checkout/checkout-processor.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import {
useCallback,
useState,
useMemo,
useReducer,
} from '@wordpress/element';
import {
emptyHiddenAddressFields,
formatStoreApiErrorMessage,
} from '@woocommerce/base-utils';
import { useDispatch, useSelect } from '@wordpress/data';
import { usePrevious } from '@woocommerce/base-hooks';
import { CHECKOUT_STORE_KEY } from '@woocommerce/block-data';

/**
Expand All @@ -28,6 +30,10 @@ import { usePaymentMethodDataContext } from './payment-methods';
import { useValidationContext } from '../validation';
import { useStoreCart } from '../../hooks/cart/use-store-cart';
import { useStoreNoticesContext } from '../store-notices';
import { reducer as emitReducer } from '../../../../data/checkout/events';
import { STATUS } from '../../../../data/checkout/constants';
import { useCheckoutNotices } from '../../hooks/use-checkout-notices';

/**
* CheckoutProcessor component.
*
Expand Down Expand Up @@ -55,11 +61,29 @@ const CheckoutProcessor = () => {
};
} );

const [ observers ] = useReducer( emitReducer, {} );
const currentObservers = useRef( observers );

const checkoutActions = useDispatch( CHECKOUT_STORE_KEY );
const checkoutState = useSelect( ( select ) =>
select( CHECKOUT_STORE_KEY ).getCheckoutState()
);

if ( redirectUrl && redirectUrl !== checkoutState.redirectUrl ) {
checkoutActions.setRedirectUrl( redirectUrl );
}

const {
checkoutNotices,
paymentNotices,
expressPaymentNotices,
} = useCheckoutNotices();

const { setCustomerId, setHasError, processCheckoutResponse } = useDispatch(
CHECKOUT_STORE_KEY
);

const { hasValidationErrors } = useValidationContext();
const { hasValidationErrors, setValidationErrors } = useValidationContext();
const { shippingErrorStatus } = useShippingDataContext();
const { billingData, shippingAddress } = useCustomerDataContext();
const { cartNeedsPayment, cartNeedsShipping, receiveCart } = useStoreCart();
Expand All @@ -80,7 +104,10 @@ const CheckoutProcessor = () => {
const [ isProcessingOrder, setIsProcessingOrder ] = useState( false );

const paymentMethodId = useMemo( () => {
const merged = { ...expressPaymentMethods, ...paymentMethods };
const merged = {
...expressPaymentMethods,
...paymentMethods,
};
return merged?.[ activePaymentMethod ]?.paymentMethodId;
}, [ activePaymentMethod, expressPaymentMethods, paymentMethods ] );

Expand Down Expand Up @@ -118,6 +145,7 @@ const CheckoutProcessor = () => {
setHasError,
] );

// Keep shipping, billing and redirectUrl current
useEffect( () => {
currentBillingData.current = billingData;
currentShippingAddress.current = shippingAddress;
Expand Down Expand Up @@ -152,6 +180,7 @@ const CheckoutProcessor = () => {
shippingErrorStatus.hasError,
] );

// Validate the checkout fields via the onCheckoutValidationBeforeProcessing callback
useEffect( () => {
let unsubscribeProcessing;
if ( ! isExpressPaymentMethodActive ) {
Expand All @@ -171,10 +200,11 @@ const CheckoutProcessor = () => {
isExpressPaymentMethodActive,
] );

// redirect when checkout is complete and there is a redirect url.
// Redirect when checkout is complete and there is a redirect url.
useEffect( () => {
if ( currentRedirectUrl.current ) {
window.location.href = currentRedirectUrl.current;
console.log( "Complete! but Don't redirect" );
// window.location.href = currentRedirectUrl.current;
}
}, [ checkoutIsComplete ] );

Expand Down Expand Up @@ -249,7 +279,10 @@ const CheckoutProcessor = () => {
}
createErrorNotice(
formatStoreApiErrorMessage( response ),
{ id: 'checkout', context: 'wc/checkout' }
{
id: 'checkout',
context: 'wc/checkout',
}
);
response?.additional_errors?.forEach?.(
( additionalError ) => {
Expand All @@ -275,7 +308,10 @@ const CheckoutProcessor = () => {
'woo-gutenberg-products-block'
)
),
{ id: 'checkout', context: 'wc/checkout' }
{
id: 'checkout',
context: 'wc/checkout',
}
);
}
setHasError( true );
Expand Down Expand Up @@ -307,6 +343,67 @@ const CheckoutProcessor = () => {
}
}, [ processOrder, paidAndWithoutErrors, isProcessingOrder ] );

// Set observers on ref so it's always current.
useEffect( () => {
currentObservers.current = observers;
}, [ observers ] );

// Emit CHECKOUT_VALIDATE event and set the error state based on the response of
// the registered callbacks
useEffect( () => {
checkoutActions.emitValidateEvent( {
observers: currentObservers.current,
createErrorNotice,
setValidationErrors,
} );
}, [
checkoutState.status,
setValidationErrors,
createErrorNotice,
checkoutActions,
] );

const previousStatus = usePrevious( checkoutState.status );
const previousHasError = usePrevious( checkoutState.hasError );

// Emit CHECKOUT_AFTER_PROCESSING_WITH_SUCCESS and CHECKOUT_AFTER_PROCESSING_WITH_ERROR events
// and set checkout errors according to the callback responses
useEffect( () => {
if (
checkoutState.status === previousStatus &&
checkoutState.hasError === previousHasError
) {
return;
}

if ( checkoutState.status === STATUS.AFTER_PROCESSING ) {
checkoutActions.emitAfterProcessingEvents( {
observers: currentObservers.current,
createErrorNotice,
notices: {
checkoutNotices,
paymentNotices,
expressPaymentNotices,
},
} );
}
}, [
checkoutState.status,
checkoutState.hasError,
checkoutState.redirectUrl,
checkoutState.orderId,
checkoutState.customerId,
checkoutState.orderNotes,
checkoutState.processingResponse,
previousStatus,
previousHasError,
createErrorNotice,
checkoutNotices,
expressPaymentNotices,
paymentNotices,
checkoutActions,
] );

return null;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,23 @@ import {
createContext,
useContext,
useReducer,
useRef,
useMemo,
useEffect,
useCallback,
} from '@wordpress/element';
import { usePrevious } from '@woocommerce/base-hooks';
import deprecated from '@wordpress/deprecated';
import { useDispatch, useSelect } from '@wordpress/data';
import { useDispatch } from '@wordpress/data';
import { CHECKOUT_STORE_KEY } from '@woocommerce/block-data';

/**
* Internal dependencies
*/
import { STATUS, DEFAULT_CHECKOUT_STATE_DATA } from './constants';
import { DEFAULT_CHECKOUT_STATE_DATA } from './constants';
import type { CheckoutStateContextType } from './types';
import {
useEventEmitters,
reducer as emitReducer,
} from '../../../../../data/checkout/events';
import { useValidationContext } from '../../validation';
import { useStoreEvents } from '../../../hooks/use-store-events';
import { useCheckoutNotices } from '../../../hooks/use-checkout-notices';
import { useEmitResponse } from '../../../hooks/use-emit-response';
import { CheckoutState } from '../../../../../data/checkout/default-state';

const CheckoutContext = createContext( DEFAULT_CHECKOUT_STATE_DATA );

Expand All @@ -52,44 +45,16 @@ export const CheckoutStateProvider = ( {
children: React.ReactChildren;
redirectUrl: string;
} ): JSX.Element => {
const checkoutActions = useDispatch( CHECKOUT_STORE_KEY );
const checkoutState: CheckoutState = useSelect( ( select ) =>
select( CHECKOUT_STORE_KEY ).getCheckoutState()
);

if ( redirectUrl && redirectUrl !== checkoutState.redirectUrl ) {
checkoutActions.setRedirectUrl( redirectUrl );
}

const { setValidationErrors } = useValidationContext();
const { createErrorNotice } = useDispatch( 'core/notices' );

const { dispatchCheckoutEvent } = useStoreEvents();
const {
isSuccessResponse,
isErrorResponse,
isFailResponse,
shouldRetry,
} = useEmitResponse();
const {
checkoutNotices,
paymentNotices,
expressPaymentNotices,
} = useCheckoutNotices();
const checkoutActions = useDispatch( CHECKOUT_STORE_KEY );

const [ observers, observerDispatch ] = useReducer( emitReducer, {} );
const currentObservers = useRef( observers );
const {
onCheckoutAfterProcessingWithSuccess,
onCheckoutAfterProcessingWithError,
onCheckoutValidationBeforeProcessing,
} = useEventEmitters( observerDispatch );

// set observers on ref so it's always current.
useEffect( () => {
currentObservers.current = observers;
}, [ observers ] );

/**
* @deprecated use onCheckoutValidationBeforeProcessing instead
*
Expand All @@ -111,66 +76,6 @@ export const CheckoutStateProvider = ( {
};
}, [ onCheckoutValidationBeforeProcessing ] );

// Emit CHECKOUT_VALIDATE event and set the error state based on the response of
// the registered callbacks
useEffect( () => {
checkoutActions.emitValidateEvent( {
observers: currentObservers.current,
createErrorNotice,
setValidationErrors,
} );
}, [
checkoutState.status,
setValidationErrors,
createErrorNotice,
checkoutActions,
] );

const previousStatus = usePrevious( checkoutState.status );
const previousHasError = usePrevious( checkoutState.hasError );

// Emit CHECKOUT_AFTER_PROCESSING_WITH_SUCCESS and CHECKOUT_AFTER_PROCESSING_WITH_ERROR events
// and set checkout errors according to the callback responses
useEffect( () => {
if (
checkoutState.status === previousStatus &&
checkoutState.hasError === previousHasError
) {
return;
}

if ( checkoutState.status === STATUS.AFTER_PROCESSING ) {
checkoutActions.emitAfterProcessingEvents( {
observers: currentObservers.current,
createErrorNotice,
notices: {
checkoutNotices,
paymentNotices,
expressPaymentNotices,
},
} );
}
}, [
checkoutState.status,
checkoutState.hasError,
checkoutState.redirectUrl,
checkoutState.orderId,
checkoutState.customerId,
checkoutState.orderNotes,
checkoutState.processingResponse,
previousStatus,
previousHasError,
createErrorNotice,
isErrorResponse,
isFailResponse,
isSuccessResponse,
shouldRetry,
checkoutNotices,
expressPaymentNotices,
paymentNotices,
checkoutActions,
] );

const onSubmit = useCallback( () => {
dispatchCheckoutEvent( 'submit' );
checkoutActions.setBeforeProcessing();
Expand Down

0 comments on commit 059533d

Please sign in to comment.