'Stripe cards elements keep refreshing when I type

I use two components for my checkout flow. I integrated Stripe card elements but whenever I type a card number, it refreshes at least 3 times before it stops and when I submit my data it doesn't seem to be well capture by my headless commerce. I followed this doc

When I submit this error shows:

v3:1 Uncaught (in promise) IntegrationError: We could not retrieve data from the specified Element.
          Please make sure the Element you are attempting to use is still mounted.
at et (v3:1:94950)
at e._handleMessage (v3:1:103953)
at e._handleMessage (v3:1:74083)
at v3:1:101904

This is my PaymentOptions component - level 3 (inside UserPayment component):

//Required
import React, { useState, useContext } from "react";
import { loadStripe } from '@stripe/stripe-js';
import { Elements } from "@stripe/react-stripe-js";
import getConfig from 'next/config';
import { useRouter } from 'next/router';
import * as ga from '../../../lib/ga/index.js';
import { uid } from 'uid';
import CheckoutForm from './Checkout_Form_Custom';

//Styles
import { Flex, Box } from 'reflexbox';
import styled from '@emotion/styled';
import { LoaderInBlue, PaypalButton, CustomCardButton } from 'components/GlobalStyles/Buttons';
import { SimpleErrorMessage } from 'components/GlobalStyles/Errors'

//Context
import EcommerceContext from '../../../context/EcommerceContext';

const { publicRuntimeConfig } = getConfig();
const stripePromise = loadStripe(publicRuntimeConfig.STRIPE_PK_TEST);

function PaymentOptions({formData, token, cartItems}){

    const router = useRouter();

    const [processing, setProcessing] = useState('');
    const [isStripe, setIsStripe] = useState(false);
    const [isPaypal, setIsPaypal] = useState(false);
    
    // Add auth context
    const { error, buttonProcessing } = useContext(EcommerceContext)

    const handleStripeButton = async (event) =>{
        event.preventDefault();
        setIsStripe(true);
         
        const generateNewTransactionId = uid()
        const totalPrice = cartItems && cartItems.length > 0 && cartItems.reduce((a, c) => a + c.unitPrice * c.quantity, 0); 
    }

    const handlePaypalButton = async (event) =>{
        event.preventDefault();
        setIsStripe(false);
    }

    return( 
        <PaymentOptionsStyled className="payment-options">
            {buttonProcessing &&  
            /** if something is loading, show the loading icon**/
                <Flex justifyContent="center">
                    <LoaderInBlue />
                </Flex>
            }

            {error && <SimpleErrorMessage />}

            { 
                !isStripe && !isPaypal && 
                /** if no payment is selected, show payment buttons **/
                <>
                    <Flex justifyContent="center" className="card-option payment-option">
                        <CustomCardButton 
                            handleStripeButton={handleStripeButton} 
                        />
                    </Flex>

                    <Flex justifyContent="center" className="paypal-option payment-option">
                        <PaypalButton
                            handlePaypalButton={handlePaypalButton} 
                            formData={formData} 
                            cartItems={cartItems}
                        />
                    </Flex>
                </>
            }

            { 
                isStripe &&
                /**** if card payment is selected, show show Stripe Elements */
                <Box className='center-box'>
                    <Flex justifyContent="center">
                        <Elements /*options={options}*/ stripe={stripePromise}>
                            <CheckoutForm token={token} />
                        </Elements>
                    </Flex>
                    <Flex my={20}>
                        <Box as="button" onClick={()=> setIsStripe(false)}>
                            {'Retour'}
                        </Box>
                    </Flex>
                </Box>
            }
        </PaymentOptionsStyled>
    )
}

const PaymentOptionsStyled = styled.div`   
 ...
`

export default PaymentOptions

This is my CheckoutForm component - last level (inside PaymentOptions component):

//Required
import React, { useState, useEffect, useContext } from "react";
import getConfig from 'next/config';
import swell from 'swell-js';
import { useRouter } from 'next/router';
import { 
    useStripe, 
    useElements, 
    CardElement 
} from "@stripe/react-stripe-js";
import { success } from 'components/Utils/pageUrls';
import {
    submitOrder
} from '../../../context/CommerceServices';
import {
    newIntent
} from 'components/Utils/apis';

//Styles
import { Flex, Box } from 'reflexbox';
import styled from '@emotion/styled';
import Checkmark  from 'components/GlobalStyles/Animations/Checkmark';
import { LoaderInBlue, PayOrder, WaitingForPayment, SubmitPaymentButton } from 'components/GlobalStyles/Buttons';

//SEO & Cookies
import { setCookie, parseCookies, destroyCookie } from 'nookies';

//Contexts
import EcommerceContext from '../../../context/EcommerceContext';

const { API_URL } = process.env;


function CheckoutForm({formData, token, getPaymentIntentId}){

    //Commerce context
    const {
        cartItems,
        cart,
        errorBeforePayment,
        setErrorBeforePayment,
        paymentIntent,
        setPaymentIntent,
    } = useContext(EcommerceContext);

    //Defines states
    const router = useRouter();
    const [isData, setIsData] = useState(false);
    const [succeeded, setSucceeded] = useState(false);
    const [error, setError] = useState(null);
    const [tokenized, setTokenized] = useState(null);
    const [processing, setProcessing] = useState('');
    const [disabled, setDisabled] = useState(true);
    const [newOrderId, setNewOrderId] = useState('');
    const [clientSecret, setClientSecret] = useState('');
    const elements = useElements();
    const stripe = useStripe();

    //Fetch stripe paymentIntent
    useEffect(async(ctx) => {

        //Cart informations
        const intentData = {
            cartItems, 
            cart
        }

        //if user is logged in, fetch payment intent
        if(token){
            // Create paymentIntent as soon as the page loads
            paymentIntent = await fetch(`${API_URL}${newIntent}`, {   
                method: 'POST',
                body: JSON.stringify({intentData}),
                headers: {
                    'Content-type': 'application/json',
                    'Authorization': `Bearer ${token}`
                }
            })
            .then(res => {
                //console.log('res payment:', res)
                return res.json();
            })
            .then(data => {
                console.log('data payment:', data)
                if(data.info && data.info.clientSecret){
                    setClientSecret(data.info.clientSecret);
                    setPaymentIntent(data.info.newId);
                    setIsData(true)
                }else{
                    setIsData(false)
                }
                
                if(data.info && data.info.newId){
                    setCookie(ctx, 'paymentIntentId', data.info.newId)
                }

                if(data.error){
                    setErrorBeforePayment(true)
                    
                }
            });
        }else{
            return
        }

    }, []);

    //Get form
    const form = document.getElementById('payment-form');
 
    //Tokenize card details with Swell
    form && form.addEventListener('submit', function(event) {
        event.preventDefault();
        console.log('adding listener');
        console.log('form', form)
        
        //raise flag!
        setProcessing(true);
        
        console.log('before tokenize');
        const tokenize = swell.payment.tokenize({
            card: {
                onError: (err) => {
                    //inform the customer there was an error
                    console.log('error tokenizing:', err);
                },
                onSuccess: () => {
                    //submit the form
                    console.log('succes tokenizing:', );
                    setTokenized(true);
                    if(tokenized){
                        console.log('tokenized so submit:', tokenized)
                        form.submit();
                    }
                }
            }
        });
        console.log('end listener');
        setProcessing(false);
    });

    
    //Submit payment to Stripe and handle error
    const handleSubmit = async ev => {
        ev.preventDefault();

        //raise flag!
        setProcessing(true);

        console.log('in submit func')

        if(clientSecret && tokenized){
            //if client secret is received, submit form
            console.log('submiting payment to stripe')
            const payload = await stripe.confirmCardPayment(clientSecret, {
                    payment_method: {
                    card: elements.getElement(CardElement)
                }
            });
            console.log('end payload:')

            if (payload && payload.error) {
                setError(`Payment failed ${payload.error.message}`);
                setProcessing(false);
            } else {
                console.log('succeed !:', payload)
                setError(null);
                setProcessing(false);
                
                //const submitOrder = await swell.cart.submitOrder()
                
                setSucceeded(true);
                destroyCookie(null, 'paymentIntentId');
            }
        }else{
            //if tokenization was unsuccessful
            console.log('not tokenized so...bye!')
            return
        }
    };

    //handle payment success
    /*if(succeeded) return (
        <>
            <CheckoutFormStyled>
                <Flex flexDirection="column" justifyContent="center">
                    <p className=" checkout-success center">Paiement réussi!</p>
                    <Checkmark />
                </Flex>
            </CheckoutFormStyled>
            {setTimeout(()=> router.push(success + '?orderId=' + `${newOrderId}&si=${successId}` ), 2000)}
        </>
    )*/

    //Render Stripe Card Element
    const customCardElement = swell && swell.payment.createElements({
        card: {
            
            elementId: '#card-element-id', // default: #card-element
            options: {
                style: {
                    base: {
                        //iconColor: '#c4f0ff',
                        fontWeight: 500,
                        fontFamily: 'Ideal Sans, system-ui, sans-serif',
                        fontSize: "16px",
                    },
                },
            },
            onChange: event => {
                // optional, called when the Element value changes
                // Listen for changes in the CardElement
                // and display any errors as the customer types their card details
                //event.preventDefault();

                setDisabled(event.empty);
                setError(event.error ? event.error.message : "");
                console.log('changing field !', event)
            },
            onSuccess: result => {
                // optional, called on card payment success
                console.log('result !');
            },
            onError: error => {
                // optional, called on card payment error
                console.log('error !');
                setError(`Payment failed`);
                setProcessing(false);
            }
        }
    });

    
    return(
        <CheckoutFormStyled>
            {
                <form id="payment-form" onSubmit={(ev) => handleSubmit(ev)}>
                    
                    {
                        !isData &&
                        /** if there is no clientSecret yet, show the loading icon **/
                        <Flex justifyContent="center"><LoaderInBlue /></Flex>
                    }
                    
                    {
                        isData &&
                        /** When the clientSecret is ready, show Stripe CardElement and payment button**/
                        <>
                            <CardElement id="card-element-id"/>
                            <button as="button" className="ck-button order payment order-form-square"
                                disabled={processing || disabled || succeeded}
                                type="submit"
                            >   
                                <Flex justifyContent='center' my={10}>
                                    { processing ?  
                                        <WaitingForPayment /> 
                                            : 
                                        <PayOrder className="order-button-content"/>
                                    }
                                </Flex>
                            </button>
                        </>
                        
                    }
                        
                    
                        {/* Show any error that happens when processing the payment */}
                        {error && 
                            <Box as="div" className="card-error" role="alert">
                                {error}
                            </Box>
                        }

                        {/* Show a success message upon completion */}
                        <Box as ="p" className={succeeded ? "result-message" : "result-message hidden"}>
                            Payé avec succès ! see result in your
                            <Box as="a"
                                href={`https://dashboard.stripe.com/test/payments`}
                            >
                                {" "}
                                Stripe dashboard.
                            </Box> Refresh the page to pay again.
                        </Box>
                </form>
            }

            {errorBeforePayment &&

                <Box>Une erreur est survenue. Veuillez réessayer ou contacter Famous in Vogue.</Box>
            }
        </CheckoutFormStyled>
    )

}

const CheckoutFormStyled = styled.div`
 ...
`

export default CheckoutForm

and lastly, this is my MultiStep component - level 1 (parent) UserPayment is level 2:

//Required
import { useContext } from 'react';
import { useForm, useStep } from 'react-hooks-helper';
import { UserIdentity, UserIdentityReview } from './Steps/User_Identity_Custom';
import { UserPaymentInactive, UserPayment } from './Steps/User_Payment_Custom';
import OrderSummary from 'components/Ecommerce/Order/Summary';

//Styles
import styled from '@emotion/styled';
import {  Flex, Box } from 'reflexbox';

//contexts
import BreakPointContext from '../../../context/BreakPointContext';

const steps = [
    { id: 'détails de facturation'},
    { id: 'payment'},
];

function MultiStep ({cartItems, cart, token, auth_user, getPaymentIntentId, saveCartChanges}) {

    const defaultData = {
    firstnameInput: auth_user.myAddress && auth_user.myAddress.firstname,
    lastnameInput: auth_user.myAddress && auth_user.myAddress.lastname,
    emailInput: auth_user.myAddress && auth_user.email,
    postcodeInput: '',
    streetInput: '',
    cityInput: '',
    countryInput: ''
};

    const { isBreakpoint } = useContext(BreakPointContext); 
    const [formData, setForm] = useForm(defaultData);
    const {step, navigation} = useStep({
        steps,
        initialStep: 0,
    });

    const props = { formData, setForm, navigation };
    
    if(isBreakpoint){
        //if mobile
        switch(step.id){
            case 'détails de facturation':
                return( 
                
                    <CheckoutStyled >
                        <Flex className="checkout">
                            <Box clasName="order-summary checkout-step" width={isBreakpoint ? 1/1 : 1.5/3} mx={isBreakpoint ? 0 : 20}>
                                <OrderSummary {...props} cartItems={cartItems} />
                            </Box>
                            <Box clasName="checkout-steps" width={ isBreakpoint ? 1/1 : 2/3}>
                                <Box clasName="checkout-step" my={20}>
                                    <UserIdentity {...props} auth_user={auth_user} cartItems={cartItems} cart={cart}/>
                                </Box>
                                <Box clasName="checkout-step" my={20}>
                                    <UserPaymentInactive {...props} cartItems={cartItems} cart={cart}/>
                                </Box>
                            </Box>
                        </Flex>
                    </CheckoutStyled>
                )
            case 'payment':
                return( 
                
                    <CheckoutStyled>
                        <Flex className="checkout">
                            <Box clasName="order-summary" width={isBreakpoint ? 1/1 : 1.5/3} mx={isBreakpoint ? 0 : 20}>
                                <OrderSummary {...props} cartItems={cartItems} cart={cart} saveCartChanges={saveCartChanges}/>
                            </Box>
                            <Box clasName="checkout-steps" width={ isBreakpoint ? 1/1 : 2/3}>
                                <Box clasName="checkout-step" my={20}>
                                    <UserIdentityReview {...props} auth_user={auth_user}/>
                                </Box>
                                <Box clasName="checkout-step" my={20}>
                                    <UserPayment {...props} cartItems={cartItems} cart={cart} token={token} getPaymentIntentId={getPaymentIntentId} />
                                </Box>
                            </Box>
                            
                        </Flex>
                    </CheckoutStyled>
                    
                )
        }
    }else{
        //if desktop
        switch(step.id){
            case 'détails de facturation':
                return( 
                
                    <CheckoutStyled>
                        <Flex className="checkout">
                            <Box clasName="checkout-steps" width={ isBreakpoint ? 1/1 : 2/3}>
                                <Box clasName="checkout-step" my={20}>
                                    <UserIdentity {...props} auth_user={auth_user} cartItems={cartItems} cart={cart}/>
                                </Box>
                                <Box clasName="checkout-step" my={20}>
                                    <UserPaymentInactive {...props} cartItems={cartItems} cart={cart}/>
                                </Box>
                            </Box>
                            <Box clasName="order-summary checkout-step" width={isBreakpoint ? 1/1 : 1.5/3} mx={isBreakpoint ? 0 : 20}>
                                <OrderSummary {...props} cartItems={cartItems} cart={cart}/>
                            </Box>
                        </Flex>
                    </CheckoutStyled>
                )
            case 'payment':
                return( 
                
                    <CheckoutStyled>
                        <Flex className="checkout">
                            <Box clasName="checkout-steps" width={2/3}>
                                <Box clasName="checkout-step" my={20}>
                                    <UserIdentityReview {...props} auth_user={auth_user}/>
                                </Box>
                                <Box clasName="checkout-step" my={20}>
                                    <UserPayment {...props} cartItems={cartItems} cart={cart} token={token} getPaymentIntentId={getPaymentIntentId} />
                                </Box>
                            </Box>
                            <Box clasName="order-summary" width={1.5/3}>
                                <OrderSummary {...props} cartItems={cartItems} cart={cart} saveCartChanges={saveCartChanges} />
                            </Box>
                        </Flex>
                    </CheckoutStyled>
                )
        }

    }
}

const CheckoutStyled = styled.div`
  ...
`
export default MultiStep


Solution 1:[1]

I removed all the useStates from CheckoutForm and the card payment form stopped refreshing when typing. I still have one error to fixed so my card details can be captured ! Thank you for your input.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1