Next.js ReferenceError:文档未在预渲染/写入页面上定义

问题描述 投票:0回答:1

我正在使用 Next.js 构建一个开源博客平台,但在部署过程中遇到了令人沮丧的错误。当尝试预渲染 /write 页面时,我收到以下错误:

ReferenceError: document is not defined
at Object.<anonymous> (/vercel/path0/.next/server/chunks/34.js:10601:12)

我尝试了各种解决方案,例如检查服务器端代码中的 DOM 访问并使用条件语句包装此类代码,但错误仍然存在。

附加信息:

Next.js 版本:19.4.13 项目存储库:https://github.com/MuhammadKaifNazeer/blogvarse/tree/main

这是./write/page.jsx的代码

"use client";

import Image from "next/image";
import styles from "./writePage.module.css";
import { useEffect, useState } from "react";
import "react-quill/dist/quill.bubble.css";
import { useRouter } from "next/navigation";
import { useSession } from "next-auth/react";
import {
  getStorage,
  ref,
  uploadBytesResumable,
  getDownloadURL,
} from "firebase/storage";
import { app } from "@/utils/firebase";
import ReactQuill from "react-quill";

const WritePage = () => {
  const { status } = useSession();
  const router = useRouter();

  const [open, setOpen] = useState(false);
  const [file, setFile] = useState(null);
  const [media, setMedia] = useState("");
  const [value, setValue] = useState("");
  const [title, setTitle] = useState("");
  const [catSlug, setCatSlug] = useState("");

  useEffect(() => {
    const storage = getStorage(app);
    const upload = () => {
      const name = new Date().getTime() + file.name;
      const storageRef = ref(storage, name);

      const uploadTask = uploadBytesResumable(storageRef, file);

      uploadTask.on(
        "state_changed",
        (snapshot) => {
          const progress =
            (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
          console.log("Upload is " + progress + "% done");
          switch (snapshot.state) {
            case "paused":
              console.log("Upload is paused");
              break;
            case "running":
              console.log("Upload is running");
              break;
          }
        },
        (error) => {},
        () => {
          getDownloadURL(uploadTask.snapshot.ref).then((downloadURL) => {
            setMedia(downloadURL);
          });
        }
      );
    };

    file && upload();
  }, [file]);

  if (status === "loading") {
    return <div className={styles.loading}>Loading...</div>;
  }

  if (status === "unauthenticated") {
    router.push("/");
  }

  const slugify = (str) =>
    str
      .toLowerCase()
      .trim()
      .replace(/[^\w\s-]/g, "")
      .replace(/[\s_-]+/g, "-")
      .replace(/^-+|-+$/g, "");

  const handleSubmit = async () => {
    const res = await fetch("/api/posts", {
      method: "POST",
      body: JSON.stringify({
        title,
        desc: value,
        img: media,
        slug: slugify(title),
        catSlug: catSlug || "style", 
      }),
    });

    if (res.status === 200) {
      const data = await res.json();
      router.push(`/posts/${data.slug}`);
    }
  };

  return (
    <div className={styles.container}>
      <input
        type="text"
        placeholder="Title"
        className={styles.input}
        onChange={(e) => setTitle(e.target.value)}
      />
      <select className={styles.select} onChange={(e) => setCatSlug(e.target.value)}>
        <option value="style">style</option>
        <option value="fashion">fashion</option>
        <option value="food">food</option>
        <option value="culture">culture</option>
        <option value="travel">travel</option>
        <option value="coding">coding</option>
      </select>
      <div className={styles.editor}>
        <button className={styles.button} onClick={() => setOpen(!open)}>
          <Image src="/plus.png" alt="" width={16} height={16} />
        </button>
        {open && (
          <div className={styles.add}>
            <input
              type="file"
              id="image"
              onChange={(e) => setFile(e.target.files[0])}
              style={{ display: "none" }}
            />
            <button className={styles.addButton}>
              <label htmlFor="image">
                <Image src="/image.png" alt="" width={16} height={16} />
              </label>
            </button>
            <button className={styles.addButton}>
              <Image src="/external.png" alt="" width={16} height={16} />
            </button>
            <button className={styles.addButton}>
              <Image src="/video.png" alt="" width={16} height={16} />
            </button>
          </div>
        )}
        <ReactQuill
          className={styles.textArea}
          theme="bubble"
          value={value}
          onChange={setValue}
          placeholder="Tell your story..."
        />
      </div>
      <button className={styles.publish} onClick={handleSubmit}>
        Publish
      </button>
    </div>
  );  
};

export default WritePage;

我尝试过的:

  1. DOM 访问检查:我仔细检查了我的代码,以检查预渲染期间在服务器端访问 DOM 元素(如文档)的任何尝试。
  2. 我研究了 Next.js 项目中“ReferenceError:文档未定义”错误的常见原因,并探索了社区建议的各种解决方案

我的期望:

我预计 /write 页面会成功预渲染,不会出现任何错误。我希望页面能够在服务器端和客户端上正确加载并按预期运行。

next.js deployment referenceerror
1个回答
0
投票

问题:

ReferenceError:文档未定义

可能的原因:

从“react-quill”导入ReactQuill;

解决方案:

有些库不支持 NextJS,因此必须动态导入。

尝试改变:

import ReactQuill from "react-quill";

import dynamic from 'next/dynamic';
const ReactQuill = dynamic(() => import("react-quill"), 
    { 
        loading: () => <p>Editor Loading...</p>, 
        ssr: false 
    }
);

请阅读:

  1. next/dynamic:https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading#nextdynamic
  2. 没有SSR:https://nextjs.org/docs/pages/building-your-application/optimizing/lazy-loading#with-no-ssr
© www.soinside.com 2019 - 2024. All rights reserved.