question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Getting "TypeError: Converting circular structure to JSON" loading PayPal script

See original GitHub issue

I have used to following solution with Nextjs and react successfully loading a PayPal script.

Preact returns this error:

Uncaught TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'AnimatedInterpolation'
    |     property 'payload' -> object with constructor 'Array'
    |     index 0 -> object with constructor 'AnimatedValue'
    |     property 'children' -> object with constructor 'Array'
    --- index 0 closes the circle
    at JSON.stringify (<anonymous>)
    at o (js?client-id=sb:1)
    at pr (js?client-id=sb:1)
    at Ar (js?client-id=sb:1)
    at n.e.getPropsRef (js?client-id=sb:1)
    at n.e.buildChildPayload (js?client-id=sb:1)
    at n.e.buildWindowName (js?client-id=sb:1)
    at js?client-id=sb:1
    at i (js?client-id=sb:1)
    at n.e.dispatch (js?client-id=sb:1)
    at n.e.then (js?client-id=sb:1)
    at js?client-id=sb:1
    at Function.n.try (js?client-id=sb:1)
    at n.e.render (js?client-id=sb:1)
    at js?client-id=sb:1
    at Function.n.try (js?client-id=sb:1)
    at r (js?client-id=sb:1)
    at Object.render (js?client-id=sb:1)
    at a.u.componentDidMount (js?client-id=sb:1)
    at N (preact.module.js:1)
    at p.dll_47b5104c397335299b17../node_modules/preact/dist/preact.module.js.p.forceUpdate (preact.module.js:1)
    at w (preact.module.js:1)

My ShoppingCart Component contains this relevant code:

const [ loaded, error ] = useScript(paypalURL)
let PayPalButton
let paypal

if (loaded) {
	paypal = window.paypal
	PayPalButton = paypal && paypal.Buttons.driver('react', { React, ReactDOM })
}

// inside return function
{loaded && (
	<PayPalButton
		style={{
		    size: 'medium', // tiny, small, medium
		    color: 'black', // orange, blue, silver
		    shape: 'rect' // pill, rect
		}}
		createOrder={(data, actions) => {
		    return actions.order.create({
			 purchase_units: [
			    {
				amount: {
				    value: '0.01'
				}
			    }
		        ]
	            })
		}}
		onApprove={(data, actions) => {
		     return actions.order.capture().then(function(details) {
			alert('Transaction completed by ' + details.payer.name.given_name)
		     })
		}}
	/>
)}

useScript code (https://usehooks.com/useScript/):

const cachedScripts = [];

function useScript(src) {
  // Keeping track of script loaded and error state
  const [state, setState] = useState({
    loaded: false,
    error: false,
  });

  useEffect(
    () => {
      // If cachedScripts array already includes src that means another instance ...
      // ... of this hook already loaded this script, so no need to load again.
      if (cachedScripts.includes(src)) {
        setState({
          loaded: true,
          error: false,
        });
      } else {
        cachedScripts.push(src);

        // Create script
        const script = document.createElement('script');
        script.src = src;
        script.async = true;

        // Script event listener callbacks for load and error
        const onScriptLoad = () => {
          setState({
            loaded: true,
            error: false,
          });
        };

        const onScriptError = () => {
          // Remove from cachedScripts we can try loading again
          const index = cachedScripts.indexOf(src);
          if (index >= 0) cachedScripts.splice(index, 1);
          script.remove();

          setState({
            loaded: true,
            error: true,
          });
        };

        script.addEventListener('load', onScriptLoad);
        script.addEventListener('error', onScriptError);

        // Add script to document body
        document.body.appendChild(script);

        // Remove event listeners on cleanup
        return () => {
          script.removeEventListener('load', onScriptLoad);
          script.removeEventListener('error', onScriptError);
        };
      }
    },
    [src], // Only re-run effect if script src changes
  );

  return [state.loaded, state.error];
}

Can you please help me fix the code so that it works?

Do I need to change this: paypal.Buttons.driver(‘react’, { React, ReactDOM }) ?

Versions: “preact”: “^10.0.0-rc.0”, “preact-render-to-string”: “^5.0.5” “preact-ssr-prepass”: “^1.0.0” “next”: “^9.0.1-canary.1”

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
robertknightcommented, Jul 23, 2019

Is there a way to implement the vanilla-js way into my React-app?

See https://reactjs.org/docs/integrating-with-other-libraries.html for general guidance on integrating UI rendered by third-party (non-React) libraries into a React app.

In brief, you need to first render a DOM element using React for use as a container, then get a reference to that DOM element and pass it to the other library to render its UI.

Using modern React “hooks” APIs this might look something like this:

import { useEffect, useRef } from "preact/hooks"

function App() {
  const paypalButtonContainer = useRef(null);
  useEffect(() => {
    // After the component is mounted, ask the PayPal scripts to render the buttons into the container
    paypal.Buttons().render(paypalButtonContainer.current);
  }, [paypalButtonContainer]);

  // Render a container for the buttons.
  return <div ref={paypalButtonContainer}></div>
}

Before trying to integrate this into your React/Preact app, you might want to try getting this working in just a plain HTML/JS app.

I have read here on GitHub that it is not a good solution due the big bundle size.

Working but slightly inefficient code beats non-working code 😛, especially when it comes to people paying you money for something.

1reaction
robertknightcommented, Jul 24, 2019

I’m closing this issue as this I think the query has been addressed.

Read more comments on GitHub >

github_iconTop Results From Across the Web

TypeError: Converting circular structure to JSON - Stack ...
It means that the object you pass in the request (I guess it is pagedoc ) has a circular reference, something like: var...
Read more >
What is TypeError: Converting circular structure to JSON?
TypeError : Converting circular structure to JSON occurs when you try to reference your variable name within the JSON object.
Read more >
what is TypeError: Converting circular structure to JSON
The error means that the object you pass in the request has a circular reference, something like: var a = {}; a.b =...
Read more >
JavaScript TypeError - Cyclic object value - GeeksforGeeks
This JavaScript exception cyclic object value occurs if the references of objects were found in JSON. JSON.stringify() fails to solve them.
Read more >
[Solved]-How to make sequential api calls in ionic2
refCont() in constructor and withLatestFrom in load. ... get method typescript · `TypeError: Converting circular structure to JSON` error when writing tests ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found