-
Notifications
You must be signed in to change notification settings - Fork 81
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Getting "Error: zoid destroyed all" in the console and only 1 button on the page renders. #30
Comments
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. |
I bailed in the end, and implemented a redux cart and payment page with just a single button on. |
I have the same problem. I can't render multiple buttons on a single page. |
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. |
I have the same issue. To try to hide the PayPal client ID and make it dynamic depending on the environment, I removed the script tag and added a variable in the options props. Only 1 button is displayed in my pricing card (I have 2 for different pricing plan) and the error is Error: zoid destroyed all.
|
I am using the official sdk with React and met the same problem. Following RebecaRey, I set a more detailed loading state to differ three period: prepare to load, loading, loaded. Normal boolean loaded state will (I guess) let sdk load again when loading. Here is my code: // with bug
const [loaded, setLoaded] = useState(false);
useEffect(() => {
if (!loaded) {
const script = document.createElement("script");
script.src = `https://www.paypal.com/sdk/js?client-id=${
isDeveloperDev ? testID : productID
}¤cy=EUR&disable-funding=credit,card,giropay,sepa,sofort`;
script.addEventListener("load", () => setLoaded(true));
document.body.appendChild(script);
console.log("append script");
}
// ...
}), [loaded, ...] The code above will log multiple "append script". The code below logs only once. // without bug
const [loadState, setLoadState] = useState({ loading: false, loaded: false });
useEffect(() => {
if (!loadState.loading && !loadState.loaded) {
setLoadState({ loading: true, loaded: false });
const script = document.createElement("script");
script.src = `https://www.paypal.com/sdk/js?client-id=${
isDeveloperDev ? testID : productID
}¤cy=EUR&disable-funding=credit,card,giropay,sepa,sofort`;
script.addEventListener("load", () =>
setLoadState({ loading: false, loaded: true })
);
document.body.appendChild(script);
console.log("append script");
}
// ...
}), [loadState, ...] I guess it's because of deps of useEffect.
|
I tried adding the script tag to the head and removing |
This should be fixable by just checking if a script already exists including the word 'paypal', however there's a large problem with the way this library works in this regard. It allows different options for each button, and obviously the PayPal script tag has to have only one set of options. You'd have to declare in the documentation 'all buttons must have the same set of options' for my fix to work, but it would work I believe save that caveat. |
I managed to resolve that, by adding: componentWillUnmount() {
// resetting all pay-pal related properties
Object.keys(window).forEach((key) => {
if (/paypal|zoid|post_robot/.test(key)) {
// eslint-disable-next-line fp/no-delete
delete window[key];
}
});
} Not really a fix, but it helped me to stop receiving this error message. |
We are actually not using this library, just found the issue to be same. Looks like the error of PayPal itself. |
I believe it occurs when multiple instances of the SDK are loaded in and have suggested a fix as well as the caveats but have not had time to attempt a fix. |
This and/or a combination of loading or unloading the paypal script on component lifecycle events seem to be valid workarounds for this issue 👍 |
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:
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:
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:
Note: this also works on Next.js. |
In your paypalButton component use this useEffect(()=> {
|
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. 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. |
I too faced similar kind of error and I just tried to add the script tag at highest level of hierarchy i.e. on the page element and voila it worked. Hope all of you guys get your issues solved. |
Quick Note: if you want to know the script is not loaded, Check |
If you embed code create button, this is my solution
|
With turbo/turbolinks it was reloading the script so I removed it from the DOM: script.onload = () => {
script.remove()
} Full code: initialize() {
if (!window.paypal && !this.loadingValue) {
this.loadingValue = true
console.log("LOADING", this.element)
this.loadScript()
}
}
loadScript() {
const cspNonce = document.querySelector("meta[name=csp-nonce]")?.content
const script = document.createElement("script")
script.setAttribute("src", this.data.get("script-url"))
script.setAttribute("data-csp-nonce", cspNonce)
script.onload = () => {
script.remove()
}
document.body.appendChild(script)
} (It's in a stimulus controller) |
Why not just add a check to see if paypal has already been loaded here and setButtonReady(true)? https://github.com/Luehang/react-paypal-button-v2/blob/master/src/index.tsx#L234 |
I had same issue, i put my "client-id" in <script> (index.html) and in PayPalScriptProvider options, just removed from PayPalScriptProvider and it's work |
There is a line in the JS SDK debug script that is the cause of the error and it states: "Attempted to load sdk version 5.0.335 on page, but window." + namespace + " at version " + (existingNamespace && existingNamespace.version) + ' already loaded.\n\nTo load this sdk alongside the existing version, please specify a different namespace in the script tag, e.g. <script src="https://www.paypal.com/sdk/js?client-id=CLIENT_ID" data-namespace="paypal_sdk"><\/script>, then use the paypal_sdk namespace in place of paypal in your code. It tests on existing namespace var existingNamespace = window[namespace]; and destroys the old script if already present. So it should work, if the new script (which might be necessary due to other options) is loaded in a new namespace. In our case we needed one script with |
bless you. been on this for a few hrs now. turns out i was still loading the initial i used to test in the index.html file while having another one going in a react component. |
Have you seen this? Is there a fix? My implementation is:
The text was updated successfully, but these errors were encountered: