Juan
Juan

Reputation: 139

Only one element of type cardNumber can be created

I am trying to display my stripe component, but I am getting this error:

IntegrationError: Only one element of type cardNumber can be created.

I don't know why, since I'm only using it once in my entire app

Any ideas?

This is my index

import ReactDOM from 'react-dom';
import App from './App';

import * as serviceWorker from './serviceWorker';
import { loadStripe } from "@stripe/stripe-js";
import { Elements } from "@stripe/react-stripe-js";
import MyComponent from './components/StripeComponent';


const promise = loadStripe("xxx-xxx-xxx");
ReactDOM.render(
  <React.StrictMode>
      <Elements stripe={promise}>
        <MyComponent/>
      </Elements>
  </React.StrictMode>,
  document.getElementById('root')
);

And this is my component

import React from "react";
import {
  useElements,
} from "@stripe/react-stripe-js";

const MyComponent: React.FC= (props)=>{

  const elements = useElements();


 
    const cardNumberElement = elements?.create('cardNumber', {
      placeholder: ''
    });
    const cardExpiryElement = elements?.create('cardExpiry', {
      placeholder: ''
    });
    const cardCvvElement = elements?.create('cardCvc', {
      placeholder: ''
    });
    
    cardNumberElement?.mount('#card-number-element')
    cardExpiryElement?.mount('#card-expiry-element')
    cardCvvElement?.mount('#card-cvv-element')

 


  const handleSubmit = async (ev: any) => {
    ev.preventDefault();
    

  };
  return (
    <form id="payment-form" onSubmit={handleSubmit}>
      <div id="card-number-element"></div>
      <div id="card-expiry-element"></div>
      <div id="card-cvv-element"></div>
    </form>

  );
}

export default MyComponent

Upvotes: 4

Views: 4861

Answers (3)

Andrii Svirskyi
Andrii Svirskyi

Reputation: 376

useEffect(() => {
  if (elements) {
    const cardNumberElement =
      elements.getElement("cardNumber") || // check if we already created element
      elements.create("cardNumber", defaultInputStyles); // create if dont

    cardNumberElement.mount("#numberInput");
  }
}, [elements]);

Upvotes: 1

Rodolfo Ortiz
Rodolfo Ortiz

Reputation: 33

I had the same problem, in my case, I had a reference to CardNumberElement in another section of my code. After removing it, everything worked fine.

Upvotes: 0

Drew Reese
Drew Reese

Reputation: 202595

Seems it is because you create and mount the card components in the functional component body they are executed on every render of the component, i.e. as an inadvertent side-effect.

Place the creation and mounting logic in an useEffect hook with an empty dependency array so that it is invoked only once when the component mounts.

import React, { useEffect } from "react";
import { useElements } from "@stripe/react-stripe-js";

const MyComponent: React.FC = (props) => {
  const elements = useElements();

  // Effect hook to run once on component mount
  useEffect(() => {
    const cardNumberElement = elements?.create("cardNumber", {
      placeholder: ""
    });
    const cardExpiryElement = elements?.create("cardExpiry", {
      placeholder: ""
    });
    const cardCvvElement = elements?.create("cardCvc", {
      placeholder: ""
    });

    cardNumberElement?.mount("#card-number-element");
    cardExpiryElement?.mount("#card-expiry-element");
    cardCvvElement?.mount("#card-cvv-element");
  }, []); // <-- empty dependency array

  const handleSubmit = async (ev: any) => {
    ev.preventDefault();
  };

  return (
    <form id="payment-form" onSubmit={handleSubmit}>
      <div id="card-number-element"></div>
      <div id="card-expiry-element"></div>
      <div id="card-cvv-element"></div>
    </form>
  );
};

Upvotes: 3

Related Questions