[英]Window is not defined in nextJS
我在 nextJS 项目中收到错误“未定义窗口”。 这里 isMobile 存储窗口大小小于 767.98 或不执行打开/关闭汉堡菜单功能的值。 此代码在 ReactJS 中运行良好,但在 NextJS 中无法运行。 请帮我弄清楚这个问题。
import Link from 'next/link';
import React, { useState, useEffect, useRef } from "react";
const Navbar = () => {
const isMobile = window.innerWidth <= 767.98;
const [isMenuOpen, setIsMenuOpen] = useState(!isMobile);
const toggle = () => isMobile && setIsMenuOpen(!isMenuOpen);
const ref = useRef()
useEffect(() => {
if (isMobile) {
const checkIfClickedOutside = (e) => {
if (!ref.current?.contains(e.target)) {
setIsMenuOpen(false);
}
};
document.addEventListener("mousedown", checkIfClickedOutside);
return () => {
// Cleanup the event listener
document.removeEventListener("mousedown", checkIfClickedOutside);
};
}
}, []);
return (
<>
<header>
<nav>
<div className="nav">
<div className="nav-brand">
<Link href="/" className="text-black"><a>Website</a></Link>
</div>
<div ref={ref}>
<div className="toggle-icon" onClick={toggle}>
<i id="toggle-button" className={isMenuOpen ? 'fas fa-times' : 'fas fa-bars'} />
</div>
{isMenuOpen && (
<div className={isMenuOpen ? "nav-menu visible" : "nav-menu"}>
<ul className="main-menu">
<li><Link href="/" onClick={toggle}><a>Home</a></Link></li>
<li><Link href="/blog" onClick={toggle}><a>Blog</a></Link></li>
<li className="drp">
<p className="dropbtn">Find <i className="fa-solid fa-angle-down"></i></p>
<ul className="dropdown-content">
<li><Link href="/find/portable-keyboards" onClick={toggle}><a>Portable Keyboards</a></Link></li>
</ul>
</li>
</ul>
</div>
)}
</div>
</div>
</nav>
</header>
</>
)
}
export default Navbar;
Next.js 是一个服务器端渲染框架,这意味着从服务器生成 HTML 的初始调用。 此时, window
对象仅在客户端可用(不在服务器端)。
要解决此问题,您需要检查window
对象的可用性。
import Link from 'next/link';
import React, { useState, useEffect, useRef } from "react";
const Navbar = () => {
const isMobile = typeof window !== "undefined" && window.innerWidth <= 767.98
const [isMenuOpen, setIsMenuOpen] = useState(!isMobile);
const toggle = () => isMobile && setIsMenuOpen(!isMenuOpen);
const ref = useRef()
useEffect(() => {
if (isMobile) {
const checkIfClickedOutside = (e) => {
if (!ref.current?.contains(e.target)) {
setIsMenuOpen(false);
}
};
document.addEventListener("mousedown", checkIfClickedOutside);
return () => {
// Cleanup the event listener
document.removeEventListener("mousedown", checkIfClickedOutside);
};
}
}, []);
return (
<>
<header>
<nav>
<div className="nav">
<div className="nav-brand">
<Link href="/" className="text-black"><a>Website</a></Link>
</div>
<div ref={ref}>
<div className="toggle-icon" onClick={toggle}>
<i id="toggle-button" className={isMenuOpen ? 'fas fa-times' : 'fas fa-bars'} />
</div>
{isMenuOpen && (
<div className={isMenuOpen ? "nav-menu visible" : "nav-menu"}>
<ul className="main-menu">
<li><Link href="/" onClick={toggle}><a>Home</a></Link></li>
<li><Link href="/blog" onClick={toggle}><a>Blog</a></Link></li>
<li className="drp">
<p className="dropbtn">Find <i className="fa-solid fa-angle-down"></i></p>
<ul className="dropdown-content">
<li><Link href="/find/portable-keyboards" onClick={toggle}><a>Portable Keyboards</a></Link></li>
</ul>
</li>
</ul>
</div>
)}
</div>
</div>
</nav>
</header>
</>
)
}
export default Navbar;
您可以修复它的另一种方法是您可以将该window
逻辑移动到useEffect
(或基于类的componentDidMount
上的 componentDidMount )
import Link from 'next/link';
import React, { useState, useEffect, useRef } from "react";
const Navbar = () => {
const [isMobile, setIsMobile] = useState(false); //the initial state depends on mobile-first or desktop-first strategy
const [isMenuOpen, setIsMenuOpen] = useState(true);
const toggle = () => isMobile && setIsMenuOpen(!isMenuOpen);
const ref = useRef()
useEffect(() => {
setIsMobile(window.innerWidth <= 767.98)
setIsMenuOpen(window.innerWidth > 767.98)
}, [])
useEffect(() => {
if (isMobile) {
const checkIfClickedOutside = (e) => {
if (!ref.current?.contains(e.target)) {
setIsMenuOpen(false);
}
};
document.addEventListener("mousedown", checkIfClickedOutside);
return () => {
// Cleanup the event listener
document.removeEventListener("mousedown", checkIfClickedOutside);
};
}
}, [isMobile]);
return (
<>
<header>
<nav>
<div className="nav">
<div className="nav-brand">
<Link href="/" className="text-black"><a>Website</a></Link>
</div>
<div ref={ref}>
<div className="toggle-icon" onClick={toggle}>
<i id="toggle-button" className={isMenuOpen ? 'fas fa-times' : 'fas fa-bars'} />
</div>
{isMenuOpen && (
<div className={isMenuOpen ? "nav-menu visible" : "nav-menu"}>
<ul className="main-menu">
<li><Link href="/" onClick={toggle}><a>Home</a></Link></li>
<li><Link href="/blog" onClick={toggle}><a>Blog</a></Link></li>
<li className="drp">
<p className="dropbtn">Find <i className="fa-solid fa-angle-down"></i></p>
<ul className="dropdown-content">
<li><Link href="/find/portable-keyboards" onClick={toggle}><a>Portable Keyboards</a></Link></li>
</ul>
</li>
</ul>
</div>
)}
</div>
</div>
</nav>
</header>
</>
)
}
export default Navbar;
请注意,使用此解决方案,您的 UI 可能会由于isMobile
状态而出现一些闪烁
您可以在父组件定义上尝试此操作。
import dynamic from 'next/dynamic'
const Navbar = dynamic(() => import('./Navbar'), { ssr: false });
const Parent = () => {
...
return (
{(typeof window !== 'undefined') &&
<Navbar/>
}
...
<Footer/>
);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.