繁体   English   中英

nextJS 中没有定义窗口

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM