ReferenceError: 在 Next.js 客户端组件内部,无法识别 document。

huangapple go评论59阅读模式
英文:

ReferenceError: document is not defined inside Next.js client component

问题

我有一个使用 Next.js 创建的客户端组件("use client")。

"use client";

import type { ReactPortal } from "react";
import { createPortal } from "react-dom";

interface PortalProps {
  children: React.ReactNode;
}

export function Portal(props: PortalProps): ReactPortal | null {
  return createPortal(props.children, document.body);
}

每当我访问使用<Portal />组件的页面时,它在控制台中抛出错误。

event - compiled client and server successfully in 469 ms (1685 modules)
ReferenceError: document is not defined
    at Portal (webpack-internal:///(sc_client)/./src/components/portal/portal.component.tsx:9:98)

我该如何修复这个问题?

附言:这是我的 package.json

// package.json
{
  // 省略部分内容
  "next": "13.1.6"
  // 省略部分内容
}
英文:

I have a client component ("use client") created using Next.js

&quot;use client&quot;

import type { ReactPortal } from &quot;react&quot;
import { createPortal } from &quot;react-dom&quot;

interface PortalProps {
  children: React.ReactNode
}

export function Portal(props: PortalProps): ReactPortal | null {
  return createPortal(props.children, document.body)
}

Whenever I visit the page which uses &lt;Portal /&gt; component it throws an error in the console

event - compiled client and server successfully in 469 ms (1685 modules)
ReferenceError: document is not defined
    at Portal (webpack-internal:///(sc_client)/./src/components/portal/portal.component.tsx:9:98)

How can I fix that?

P.S. This is my package.json

// package.json
{
  // cut
  &quot;next&quot;: &quot;13.1.6&quot;
  // cut
}

答案1

得分: 2

我刚刚发现客户端组件在服务器上预渲染,而服务器上无法访问document对象。这就是为什么会显示document is not defined的原因。

有很多方法可以解决这个问题:

  1. typeof window !== undefined检查中包装document,以确保文档仅在浏览器环境中可访问(在预渲染期间window未定义)。
  2. 使用useEffect + useState + condition

我对这个问题的解决方案如下所示:

"use client";

import { type ReactPortal, useEffect, useState } from "react"
import { createPortal } from "react-dom"

interface PortalProps {
  children: React.ReactNode
}

export function Portal(props: PortalProps): ReactPortal | null {
  const [isMounted, setIsMounted] = useState(false)

  useEffect(() => {
    setIsMounted(true)
  }, [])

  return isMounted ? createPortal(props.children, document.body) : null // createPortal将不会在服务器上渲染,只会在客户端水合后渲染
}

另请参阅https://beta.nextjs.org/docs/rendering/server-and-client-components#client-components

客户端组件

客户端组件使您能够为应用程序添加客户端端交互性。在Next.js中,它们在服务器上进行预渲染,然后在客户端进行水合。您可以将客户端组件视为Next.js 12和以前版本的工作方式(即pages/目录)。查看

英文:

I just found that Client components prerender on the server which doesn't have a access to the document object. That's why it says document is not defined.

There are many options how it can be fixed

  1. Wrap document in typeof window !== undefiend check to make sure the document is only accessible in the browser enrironment (window is undefined during prerender).
  2. Use useEffect + useState + condition

My solution to the problem looks like this

&quot;use client&quot;

import { type ReactPortal, useEffect, useState } from &quot;react&quot;
import { createPortal } from &quot;react-dom&quot;

interface PortalProps {
  children: React.ReactNode
}

export function Portal(props: PortalProps): ReactPortal | null {
  const [isMounted, setIsMounted] = useState(false)

  useEffect(() =&gt; {
    setIsMounted(true)
  }, [])

  return isMounted ? createPortal(props.children, document.body) : null // createPortal will not be rendered on the server. Only on the client after hydration
}


See also https://beta.nextjs.org/docs/rendering/server-and-client-components#client-components

> Client Components
>
> Client Components enable you to add client-side interactivity to your
> application. In Next.js, they are prerendered on the server and
> hydrated on the client. You can think of Client Components as how
> Next.js 12 and previous versions worked (i.e. the pages/ directory)
> See

huangapple
  • 本文由 发表于 2023年2月10日 15:38:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/75408148.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定