react-paypal-button-v2: Getting "Error: zoid destroyed all" in the console and only 1 button on the page renders.

Have you seen this? Is there a fix? My implementation is:

<PayPalButton
	amount="0.01"
	onSuccess={ (details, data) => {
		console.log("Transaction completed", details);
	} }
	options={ {
		clientId: 'AVYn2RUPC.....',
		currency: 'NZD',
		merchantId: '3T6K....',
	} }
	style = { {
		color: 'black',
		layout: 'horizontal'
	} }
/>

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 4
  • Comments: 22

Commits related to this issue

Most upvoted comments

I had the same error, it seems to have been caused by loading the PayPal script more than once. I fixed it by loading the script just once in the head, then loading the two buttons where they should be.

There is an easier way to solve this, if you look at the code, there is a onButtonReady prop, and also this component checks that the sdk is already present in window.paypal.

So, add this state variable:

const [paypalLoaded, setPaypalLoaded] = useState(false);

Then set to only one component ( the first one, doesn’t matter which one ) the prop onButtonReady to set the paypalLoaded state to true like so:

                <PayPalButton
                  onButtonReady={() => {
                    setPaypalLoaded(true);
                  }}

The rest of the components should check against this state variable to be rendered only after the first one loaded the SDK, so SDK is not loaded multiple times:

                { paypalLoaded
                  ? (
                    <PayPalButton
                      ... // No onButtonReady needed here...
                    />
                  ) : null }

Note: this also works on Next.js.

I have solved it with a mix of mtshin and imekinox solutions:

I have two different pages. In the first one, I only have subscriptions. In the second one, simple checkouts. On both pages, I have a useEffect hook to remove the Paypal SDK when the page unmounts. The reason to use it is that subscriptions and simple checkouts use different configuration of the SDK:

useEffect(() => {
    return () => {
      Object.keys(window).forEach(key => {
        if (/paypal|zoid|post_robot/.test(key)) {
          delete window[key];
        }
      });

      document.querySelectorAll('script[src*="www.paypal.com/sdk"]').forEach(node => node.remove());
    };
  }, []);

Also, on both pages I have the loading state of Paypal:

const [paypalSDKReady, setPaypalSDKReady] = useState(false);

Then I only render one button. The other ones are rendered when paypalSDKReady is true:

<PayPalButton
          amount={price}
          createOrder={createOrder}
          onApprove={onApprove}
          onError={onError}
          options={{clientId: process.env.REACT_APP_PAYPAL_CLIENT_ID, currency: 'EUR', vault: false, intent: 'capture'}}
          onButtonReady={() => setPaypalSDKReady(true)}
        />
{
paypalSDKReady ? (
<>
<PayPalButton
          amount={price}
          createOrder={createOrder}
          onApprove={onApprove}
          onError={onError}
          options={{clientId: process.env.REACT_APP_PAYPAL_CLIENT_ID, currency: 'EUR', vault: false, intent: 'capture'}}
        />
// Other Paypal buttons
</>
) : null}

The options parameter is required in this case. Maybe on the buttons rendered after the paypalSDKReady is true, the options parameter is not needed, but I have not tested it.

In your paypalButton component use this

useEffect(()=> {

let paypalScript = document.getElementById('paypal-script');

new Promise(resolve => {

  if(paypalScript){
    resolve(true)
  }else{
    const target = document.body;
    const tag = document.createElement('script');
    tag.async = false;
    tag.id = 'paypal-script';
    tag.src = 'https://www.paypal.com/sdk/js?client-..........';
    target.appendChild(tag);
    tag.addEventListener('load', resolve, {
      once: true
    });
  }
}).then(resp => {

I have the same problem, I am trying to implement two buttons in the same modal: one for subscriptions and one for one-time payments. Even though I pass different props to them etc. when the page is rendered I get the same error and only one button is rendered.