svelte-stripe icon indicating copy to clipboard operation
svelte-stripe copied to clipboard

Bug when mounting/unmounting Payment Elements

Open frederichoule opened this issue 1 year ago • 12 comments

Describe the bug

When creating, destroying and recreating the Payment Element, we get an error related to getContext() in Payment Elements.

[HMR][Svelte] Unrecoverable HMR error in <LinkAuthenticationElement>: next update will trigger a full reload
logError @ proxy.js?v=1f1e0bb9:15
Show 1 more frame
Show lessUnderstand this error
LinkAuthenticationElement.svelte:17 Uncaught (in promise) TypeError: Cannot destructure property 'elements' of 'getContext(...)' as it is undefined.
    at instance (LinkAuthenticationElement.svelte:17:11)
    at init (chunk-S2NM74UB.js?v=1f1e0bb9:2190:23)
    at new LinkAuthenticationElement (LinkAuthenticationElement.svelte:46:24)
    at createProxiedComponent (svelte-hooks.js?v=1f1e0bb9:341:9)
    at new ProxyComponent (proxy.js?v=1f1e0bb9:242:7)
    at new Proxy<LinkAuthenticationElement> (proxy.js?v=1f1e0bb9:349:11)
    at Array.create_default_slot (+page.svelte:105:11)
    at create_slot (chunk-S2NM74UB.js?v=1f1e0bb9:101:25)
    at create_if_block (Elements.svelte:3:45)
    at create_fragment (Elements.svelte:64:24)

Reproduction

Severity

blocking all usage

Additional Information

No response

frederichoule avatar Sep 23 '24 23:09 frederichoule

By forcing Elements.svelte to re-set the context when the variables are updated, it seems to work.

$: setContext('stripe', { stripe, elements });

frederichoule avatar Sep 23 '24 23:09 frederichoule

Tentative PR here: https://github.com/joshnuss/svelte-stripe/pull/123

frederichoule avatar Sep 24 '24 00:09 frederichoule

Hey @joshnuss did you have any chance to look into this, or find a better fix? Thanks

frederichoule avatar Sep 24 '24 22:09 frederichoule

Hi @frederichoule!

I'm wondering, what's different in your repo vs this one? Did you change anything?

Because when I try it with the live Svelte PaymentElement example it seems to work OK

joshnuss avatar Sep 27 '24 05:09 joshnuss

Did you try my repo? There's a new button in the Svelte PaymentElement example page that says "Hide/Show" and replicates the bug.

The only change to the actual payment element is there: https://github.com/joshnuss/svelte-stripe/pull/123/commits/254b636b4fbd351b64e74819ae475d39c43095a0

Line 71: $: setContext('stripe', { stripe, elements });

frederichoule avatar Sep 27 '24 14:09 frederichoule

Hey @joshnuss, the example wasn't in the repo, sorry it's my fault. It is now.

Example: https://github.com/frederichoule/svelte-stripe Fix: https://github.com/joshnuss/svelte-stripe/pull/123

frederichoule avatar Sep 27 '24 15:09 frederichoule

Any update @joshnuss ?

frederichoule avatar Oct 03 '24 19:10 frederichoule

I am getting the same error when I use Elements in a multi-step form where the user advances to the payment element step, but goes back and then again to the step with the payment element, then an error is thrown and the app stops working:

Unhandled Promise Rejection: TypeError: Right side of assignment cannot be destructured

The error stems from calling getContext in an element that returns an undefined value.

I rectified the bug by simply adding to the Elements.svelte a onDestroy lifecycle function. When unmounting the Elements component, the elements objects and its contents gets garbage collected.

onDestroy(() => { if (elements) { elements = null; } });

TheRealThor avatar Oct 12 '24 21:10 TheRealThor

Getting ready for production in the next few days, and no update from @joshnuss - so here's what I did to fix it without forking the whole library:

<script lang="ts">
	let stripeElements: StripeElements | null = $state(null);
	const svelteStripeFix = (e: HTMLDivElement) => {
		$effect(() => {
			return () => {
				stripeElements = null;
			};
		});
	};
</script>
<div use:svelteStripeFix>
  <Elements bind:elements={stripeElements}><!-- Elements here //--></Elements>
</div>

Basically, as soon as the div surrounding the Elements is destroyed, we just clear the binded reference to it and then everything works again the next time you mount it in the same payment flow.

Thanks @TheRealThor for pointing me in the right direction!

frederichoule avatar Nov 24 '24 19:11 frederichoule

@frederichoule apologies for the delay. I will take a look this week.

joshnuss avatar Nov 27 '24 14:11 joshnuss

any progress on that @joshnuss ?

frederichoule avatar Dec 12 '24 01:12 frederichoule

Ran head on into this issue last week thought it was me. Same issues as reported.

For those of you that may be struggling in tabs here's my fix.

	async function initializeStripe() {
		stripe = await loadStripe(PUBLIC_STRIPE_KEY_BASE);
	}
	
	
	function switchTab(tab: string) {
		activeTab = tab;

		if(activeTab === 'Payments') {
			elements = null;
			stripe = null;
			initializeStripe();

		}
	}

If you have a bunch of tabs for the user to cycle through just call switchTab("Payments") where needed.

vamman avatar Jan 11 '25 06:01 vamman