Next.js:文档未定义

Posted

技术标签:

【中文标题】Next.js:文档未定义【英文标题】:Next.js: document is not defined 【发布时间】:2022-01-06 23:35:09 【问题描述】:

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

文档未定义

我正在使用 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

【问题讨论】:

相关:***.com/q/35068451/12860895 【参考方案1】:

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

您需要使用验证器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 = () => 

        ;
    
;

【讨论】:

请注意,process.browser 已被弃用。您现在应该使用typeof window === 'undefined'。参考github.com/zeit/next.js/issues/5354#issuecomment-520305040【参考方案3】:

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

if (typeof window !== "undefined") 

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

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

import router from "next/client";

【讨论】:

【参考方案4】:

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

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

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

【讨论】:

正是我需要的。非常感谢!【参考方案5】:

你应该阅读 node.js 中的 JS 和 Browser 中的 JS 之间的区别。在 node.js 中,您没有 DOM API(window、document、document.getElementById、...),只有当您的 html 在称为浏览器窗口的东西中呈现时才能拥有。所以 next.js 使用 node.js 来运行 JS 代码并获取结果来渲染 HTML 文件。但是在node.js中,什么都不是Browser的窗口。

我的英语很差。但我希望这个答案对你有所帮助。

【讨论】:

【参考方案6】:

例如,当模块包含仅在浏览器中工作的库时。有时动态导入可以解决这个问题。

看看下面的例子:

import dynamic from 'next/dynamic'

const DynamicComponentWithNos-s-r = dynamic(
() => import('../components/hello3'),
 s-s-r: false 
)

function Home() 
return (
<div>
  <Header />
  <DynamicComponentWithNos-s-r />
  <p>HOME PAGE is here!</p>
</div>
 )


export default Home

【讨论】:

非常被低估的答案。接受我的投票并感谢您的帮助!【参考方案7】:

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

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

第 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>

【讨论】:

【参考方案8】:

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

【讨论】:

【参考方案9】:

这可能对其他人有帮助,当 Material UI anchorEl 为 true 时出现此错误

const [anchoeEl, setAnchorEl] = useState(null)

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

【讨论】:

以上是关于Next.js:文档未定义的主要内容,如果未能解决你的问题,请参考以下文章

Next JS - ReferenceError:文档未定义[重复]

Next.js 上下文提供程序用页面特定布局组件包装 App 组件,提供未定义的数据

next.js 环境变量未定义(Next.js 10.0.5)

Next.JS & JEST - publicRuntimeConfig 未定义

如何解决 Next.js 中的“未定义窗口”错误

React 和 Next.js 中的窗口未定义错误 [重复]