问题
I am trying to create a payment form where people can pay but I keep getting this error.
document is not defined
I'm using Next.js. Please see my code below:
import React from "react";
import {Elements, StripeProvider} from 'react-stripe-elements';
import CheckoutForm from '../../components/Payment/CheckoutForm';
import { useRouter } from 'next/router';
var stripe_load = () => {
var aScript = document.createElement('script');
aScript.type = 'text/javascript';
aScript.src = " https://js.stripe.com/v3/";
document.head.appendChild(aScript);
aScript.onload = () => {
};
};
function Payment({host}) {
const key = host.includes('localhost') ? 'test' : 't';
stripe_load();
const router = useRouter();
return (
<div className="Payment Main">
<StripeProvider apiKey={key}>
<Elements>
<CheckoutForm planid={router.query.id}/>
</Elements>
</StripeProvider>
<br/>
<br/>
<p>Powered by Stripe</p>
</div>
);
};
Payment.getInitialProps = async ctx => {
return { host: ctx.req.headers.host }
};
export default Payment
回答1:
I think, in server rendering mode, the document is undefined. You should be able to use it inside class lifecycle methods or useEffect
import React, {useEffect} from "react";
import {Elements, StripeProvider} from 'react-stripe-elements';
import CheckoutForm from '../../components/Payment/CheckoutForm';
import { useRouter } from 'next/router';
var stripe_load = () => {
var aScript = document.createElement('script');
aScript.type = 'text/javascript';
aScript.src = " https://js.stripe.com/v3/";
document.head.appendChild(aScript);
aScript.onload = () => {
};
};
function Payment({host}) {
const key = host.includes('localhost') ? 'test' : 't';
useEffect(() => {
var aScript = document.createElement('script');
aScript.type = 'text/javascript';
aScript.src = " https://js.stripe.com/v3/";
document.head.appendChild(aScript);
aScript.onload = () => {
};
}, [])
//stripe_load();
const router = useRouter();
return (
<div className="Payment Main">
<StripeProvider apiKey={key}>
<Elements>
<CheckoutForm planid={router.query.id}/>
</Elements>
</StripeProvider>
<br/>
<br/>
<p>Powered by Stripe</p>
</div>
);
};
Payment.getInitialProps = async ctx => {
return { host: ctx.req.headers.host }
};
export default Payment
回答2:
You need to wrap your document
using validator process.browser
, because this document is belong to client side, and the error occured when nextjs render in server side.
var stripe_load = () => {
if (process.browser) {
var aScript = document.createElement('script');
aScript.type = 'text/javascript';
aScript.src = " https://js.stripe.com/v3/";
document.head.appendChild(aScript);
aScript.onload = () => {
};
}
};
回答3:
You should read the difference between JS in node.js and JS in Browser. In node.js, you don't have DOM APIs (window, document, document.getElementById,...), the thing can be only have when your HTML is rendered in a thing called windows of Browsers. So next.js use node.js to run JS code and take result to render HTML file. But in node.js, nothing is windows of Browser.
My English is quite bad. But I hope this answer will be helpful for you.
回答4:
To access the document in Next.js you need await the page render first then get it in a variable, like so:
const [_document, set_document] = React.useState(null)
React.useEffect(() => {
set_document(document)
}, [])
回答5:
You have to make sure you have two things setup.
- DOM render is completed.
- Then load the function.
Step 1: create hook isMounted this will make sure that your DOM is rendered.
import React, {useEffect, useState} from 'react';
function RenderCompleted() {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true)
return () => {
setMounted(false)
}
});
return mounted;
}
export default RenderCompleted;
Inside function Payment load the hook :
import React, {useEffect} from "react";
import {Elements, StripeProvider} from 'react-stripe-elements';
import CheckoutForm from '../../components/Payment/CheckoutForm';
import { useRouter } from 'next/router';
import RenderCompleted from '../hooks/isMounted';
var stripe_load = () => {
var aScript = document.createElement('script');
aScript.type = 'text/javascript';
aScript.src = " https://js.stripe.com/v3/";
document.head.appendChild(aScript);
aScript.onload = () => {
};
};
function Payment({host}) {
const[key,setKey] = useEffect('');
//will help you on re-render or host changes
useEffect(()=>{
const key = host.includes('localhost') ? 'test' : 't';
setKey(key);
},[host])
useEffect(() => {
var aScript = document.createElement('script');
aScript.type = 'text/javascript';
aScript.src = " https://js.stripe.com/v3/";
document.head.appendChild(aScript);
aScript.onload = () => {
};
}, [isMounted])
const router = useRouter();
const isMounted = RenderCompleted();
return (
<div className="Payment Main">
<StripeProvider apiKey={key}>
<Elements>
<CheckoutForm planid={router.query.id}/>
</Elements>
</StripeProvider>
<br/>
<br/>
<p>Powered by Stripe</p>
</div>
);
};
Payment.getInitialProps = async ctx => {
return { host: ctx.req.headers.host }
};
export default Payment
Another way to do this is, to use head component of next.js: https://nextjs.org/docs/api-reference/next/head
<Head>
<title>My page title</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
</Head>
回答6:
This might be helpful to some else, I got this error when Material UI anchorEl is true
const [anchoeEl, setAnchorEl] = useState(null)
<Menu
anchorEl={anchorEl}
anchorOrigin={{
vertical: "top"
horizontal: "right"
}}
/>
来源:https://stackoverflow.com/questions/60629258/next-js-document-is-not-defined