Next.js: 文档未定义

Next.js: document is not defined

我正在尝试创建一个人们可以付款的付款表单,但我一直收到此错误。

document is not defined

我正在使用 Next.js。请在下面查看我的代码:

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

我认为,在服务器渲染模式下,文档是未定义的。 您应该能够在 class 生命周期方法或 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

您需要使用验证器 process.browser 包装您的 document,因为此文档属于客户端,而 nextjs 在服务器端呈现时发生错误。

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 = () => {

        };
    }
};

您必须确保设置了两件事。

  1. DOM渲染完成。
  2. 然后加载函数。

第 1 步:创建挂钩 isMounted 这将确保呈现您的 DOM。

import React, {useEffect, useState} from 'react';

function RenderCompleted() {

    const [mounted, setMounted] = useState(false);

    useEffect(() => {
        setMounted(true)

        return () => {
            setMounted(false)
        }
    });

    return mounted;
}

export default RenderCompleted;

内部函数支付加载钩子:

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

另一种方法是使用 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>

您应该阅读 node.js 中的 JS 和浏览器中的 JS 之间的区别。在 node.js 中,您没有 DOM API(window、文档、document.getElementById、...),只有当您的 HTML 呈现在浏览器的一个叫做 windows 的东西中。所以 next.js 使用 node.js 到 运行 JS 代码并将结果呈现给 HTML 文件。但是在node.js中,浏览器的windows什么都没有。

我的英文很烂。不过希望这个回答对你有所帮助。

要访问 Next.js 中的文档,您需要先等待页面呈现,然后将其放入变量中,如下所示:

const [_document, set_document] = React.useState(null)

React.useEffect(() => {
    set_document(document)
}, [])

这可能对其他人有帮助,当 Material UI anchorEl 为真时我得到了这个错误

const [anchoeEl, setAnchorEl] = useState(null)

<Menu 
    anchorEl={anchorEl}  
    anchorOrigin={{
        vertical: "top"
        horizontal: "right"
    }}
/>

对我来说,这个错误是由很多错误引起的: 首先,你必须使用

if (typeof window !== "undefined") {

对于每个 window.document.,尤其是对于 localStorage. 函数(我自己忘记为 localStorage.getItem 使用这个 if 子句并且错误没有修复)

另一个问题是下面一行的导入完全错误!:

import {router} from "next/client";

例如,当模块包含一个只能在浏览器中运行的库时。有时动态导入可以解决这个问题。

看看下面的例子:

import dynamic from 'next/dynamic'

const DynamicComponentWithNoSSR = dynamic(
() => import('../components/hello3'),
{ ssr: false }
)

function Home() {
return (
<div>
  <Header />
  <DynamicComponentWithNoSSR />
  <p>HOME PAGE is here!</p>
</div>
 )
}

export default Home

您可以在关闭 ssr 的情况下使用名为 dynamic imports 的新功能。这是重新渲染组件的解决方法。