英文:
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
"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)
}
Whenever I visit the page which uses <Portal />
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
"next": "13.1.6"
// cut
}
答案1
得分: 2
我刚刚发现客户端组件在服务器上预渲染,而服务器上无法访问document
对象。这就是为什么会显示document is not defined
的原因。
有很多方法可以解决这个问题:
- 在
typeof window !== undefined
检查中包装document
,以确保文档仅在浏览器环境中可访问(在预渲染期间window
未定义)。 - 使用
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
- Wrap
document
intypeof window !== undefiend
check to make sure the document is only accessible in the browser enrironment (window
is undefined during prerender). - Use
useEffect
+useState
+condition
My solution to the problem looks like this
"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 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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论