簡體   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