react-stripe-js: [BUG]: Elements component can create multiple instances of stripeJs.StripeElements
What happened?
Recent changes to this library have changed when and how often the useEffect which creates the StripeJs.StripeElements instance runs.
Because of the addition of options to the dependency list, it reruns on every render for those who have configured the Elements component with a non-memoized set of options. This a a reasonable assumption since:
- The examples in the docs do it this way
- The docs state “Once the stripe prop has been set, these options can’t be changed.” implying they are basically read once.
If we pass a promise to the Elements stripe attribute, and there are multiple renders prior to that promise resolving, the effect will run multiple times and attached multiple resolvers to the promise and therefore create multiple elements instances.
In the typical flow, the first resolution will create any child PaymentElements attached to the first elements instance.
Subsequent useElements in child instances will get the newer elements instances after that.
When trying to checkout (confirmPayment with useElements), the checkout fails and we then get the error “Invalid value for stripe.confirmPayment(): elements should have a mounted Payment Element.”
For others facing this issue, one of many ways to work around it is to wrap the options argument in a useMemo, or to pass a Stripe instance to Elements component rather than a Stripe promise.
Environment
No response
Reproduction
No response
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 8
- Comments: 23 (6 by maintainers)
We had a same issue as @martin3walker.
Code snippet which was breaking elements component:
Hello,
I’m running into this same error:
IntegrationError: Invalid value for stripe.confirmPayment(): elements should have a mounted Payment Element or Pay Button Element.I’m using version 1.16.0 of@stripe/react-stripe-js.Following the workaround advice above, I’m passing in a resolved instance of the stripe.js SDK into the
stripeprop of theElementsprovider. I’m also memoizing theoptionsobject where I pass in the client secret.In the
<StripePaymentForm />component I’m calling the following click handler to submit the data from a<PaymentElement />:The error is being thrown at
stripe.confirmPayment. The<PaymentElement />is rendered, so I’m confused as to why it says it isn’t mounted.Do we think this is the same bug? Perhaps it is a mistake on my end as I’m relatively new to Stripe 🤷
@mario-garcia:
The PaymentElement has to be mounted and on the page when calling stripe.confirmPayment. The folks above were replacing the PaymentElement with a load spinner in their pay/click handler, which was causing the
IntegrationError: Invalid value for stripe.confirmPayment(): elements should have a mounted Payment Element or Pay Button Element.error.Let me know if that clarification helps.
My problem ended up being unrelated to this. I set a loading state in the click handler that was replacing the
PaymentElementwith a loading component. This was messing with Stripe’s flow. Just FYI in case anyone else runs into a similar issue.FWIW I hit this a couple days ago too. Details here: https://stackoverflow.com/q/72334261/65387
I had the same issue.
Turns out I had accidentally used
<Elements></Elements>twice while adapting the official example to my needs.Did not have to useMemo or the like.
Dropping back to “@stripe/react-stripe-js”: “^1.7.1” until this is fixed.
aaaaahhh. thanks @bmathews-stripe. that clarifies it and indeed was my issue. thanks again.
@randypuro and @mnpenner: We’ve released a (likely) fix in v1.8.1. Thanks!
React 18.1, not using StrictMode.