reactjs 使用@tiptap/react库将Next.js(V13)部署到Vercel时出错

hc8w905p  于 2023-06-05  发布在  React
关注(0)|答案(1)|浏览(195)

我试图将我的NextJs(V13)部署到Vercel,但它给了我这个错误:图像(https://i.stack.imgur.com/iLZSA.png
问题似乎来自useEditor,它是@tiptap/react库的,这是我的创建页面:

import Editor, { FinalPost } from "@/components/editor";
import AdminLayout from "@/components/layout/AdminLayout";
import { generateFormData } from "@/utils/helper";
import axios from "axios";
import { NextPage } from "next";
import { useRouter } from "next/router";
import { useState } from "react";

interface Props {}

const Create: NextPage<Props> = () => {
  const [creating, setCreating] = useState(false);
  const router = useRouter();

  const handleSubmit = async (post: FinalPost) => {
    setCreating(true);
    try {
      // we have to generate FormData
      const formData = generateFormData(post);

      // submit our post
      const { data } = await axios.post("/api/posts", formData);
      router.push("/admin/posts/update/" + data.post.slug);
    } catch (error: any) {
      console.log(error.response.data);
    }
    setCreating(false);
  };

  return (
    <AdminLayout title="New Post">
      <div className="max-w-4xl mx-auto">
        <Editor onSubmit={handleSubmit} busy={creating} />
      </div>
    </AdminLayout>
  );
};

export default Create;

这是我的编辑器组件:

import { ChangeEventHandler, FC, useEffect, useState } from "react";
import { useEditor, EditorContent, getMarkRange, Range } from "@tiptap/react";
import axios from "axios";
import StarterKit from "@tiptap/starter-kit";
import Underline from "@tiptap/extension-underline";
import Placeholder from "@tiptap/extension-placeholder";
import TiptapImage from "@tiptap/extension-image";
import Link from "@tiptap/extension-link";
import Youtube from "@tiptap/extension-youtube";
import Toolbar from "./Toolbar";
import EditLink from "./Link/EditLink";
import GallaryModal, { ImageSelectionResult } from "./GalleryModal";
import SeoForm, { SeoResult } from "./SeoForm";
import ActionButton from "../common/ActionButton";
import ThumbnailSelector from "./ThumbnailSelector";

export interface FinalPost extends SeoResult {
  id?: string;
  title: string;
  content: string;
  thumbnail?: File | string;
}

interface Props {
  onSubmit(post: FinalPost): void;
  initialValue?: FinalPost;
  busy?: boolean;
  btnTitle?: string;
}

const Editor: FC<Props> = ({
  onSubmit,
  initialValue,
  busy = false,
  btnTitle = "Submit",
}): JSX.Element => {
  const [selectionRange, setSelectionRange] = useState<Range>();
  const [showGallery, setShowGallery] = useState(false);
  const [uploading, setUploading] = useState(false);
  const [images, setImages] = useState<{ src: string }[]>([]);
  const [seoInitialValue, setSeoInitialValue] = useState<SeoResult>();
  const [post, setPost] = useState<FinalPost>({
    title: "",
    content: "",
    meta: "",
    tags: "",
    slug: "",
  });

  const fetchImages = async () => {
    const { data } = await axios("/api/image");
    setImages(data.images);
  };

  const handleImageUpload = async (image: File) => {
    setUploading(true);
    const formData = new FormData();
    formData.append("image", image);
    const { data } = await axios.post("/api/image", formData);
    setUploading(false);
    setImages([data, ...images]);
  };

  const editor = useEditor({
    extensions: [
      StarterKit,
      Underline,
      Link.configure({
        autolink: false,
        linkOnPaste: false,
        openOnClick: false,
        HTMLAttributes: {
          target: "",
        },
      }),
      Placeholder.configure({
        placeholder: "Type something …",
      }),
      Youtube.configure({
        width: 840,
        height: 472.5,
        HTMLAttributes: {
          class: "mx-auto rounded",
        },
      }),
      TiptapImage.configure({
        HTMLAttributes: {
          class: "mx-auto",
        },
      }),
    ],
    editorProps: {
      handleClick(view, pos, event) {
        const { state } = view;
        const selectionRange = getMarkRange(
          state.doc.resolve(pos),
          state.schema.marks.link
        );
        if (selectionRange) setSelectionRange(selectionRange);
      },
      attributes: {
        class:
          "prose prose-lg focus:outline-none dark:prose-invert max-w-full mx-auto h-full",
      },
    },
  });

  const handleImageSelection = (result: ImageSelectionResult) => {
    editor
      ?.chain()
      .focus()
      .setImage({ src: result.src, alt: result.altText })
      .run();
  };

  const handleSubmit = () => {
    if (!editor) return;
    onSubmit({ ...post, content: editor.getHTML() });
  };

  const updateTitle: ChangeEventHandler<HTMLInputElement> = ({ target }) =>
    setPost({ ...post, title: target.value });

  const updateSeoValue = (result: SeoResult) => setPost({ ...post, ...result });

  const updateThumbnail = (file: File) => setPost({ ...post, thumbnail: file });

  useEffect(() => {
    if (editor && selectionRange) {
      editor.commands.setTextSelection(selectionRange);
    }
  }, [editor, selectionRange]);

  useEffect(() => {
    fetchImages();
  }, []);

  useEffect(() => {
    if (initialValue) {
      setPost({ ...initialValue });
      editor?.commands.setContent(initialValue.content);
      const { meta, slug, tags } = initialValue;

      setSeoInitialValue({ meta, slug, tags });
    }
  }, [initialValue, editor]);

  return (
    <>
      <div className="p-3 dark:bg-primary-dark bg-primary transition">
        <div className="sticky top-0 z-10 dark:bg-primary-dark bg-primary">
          {/* thumbnail selector */}
          <div className="flex items-center justify-between mb-3">
            <ThumbnailSelector
              onChange={updateThumbnail}
              initialValue={post.thumbnail as string}
            />
            <div className="inline-block">
              <ActionButton
                busy={busy}
                title={btnTitle}
                onClick={handleSubmit}
                disabled={busy}
              />
            </div>
          </div>

          {/* title input */}
          <input
            type="text"
            className="py-2 outline-none bg-transparent w-full border-0 border-b-[1px] border-secondary-dark dark:border-secondary-light text-3xl font-semibold italic text-primary-dark dark:text-primary mb-3"
            placeholder="Title"
            onChange={updateTitle}
            value={post.title}
          />
          <Toolbar
            editor={editor}
            onOpenImageClick={() => setShowGallery(true)}
          />
          <div className="h-[1px] w-full bg-secondary-dark dark:bg-secondary-light my-3" />
        </div>

        {editor ? <EditLink editor={editor} /> : null}
        <EditorContent editor={editor} className="min-h-[300px]" />
        <div className="h-[1px] w-full bg-secondary-dark dark:bg-secondary-light my-3" />
        <SeoForm
          onChange={updateSeoValue}
          title={post.title}
          initialValue={seoInitialValue}
        />
      </div>

      <GallaryModal
        visible={showGallery}
        onClose={() => setShowGallery(false)}
        onSelect={handleImageSelection}
        images={images}
        onFileSelect={handleImageUpload}
        uploading={uploading}
      />
    </>
  );
};

export default Editor;

关于我的package.json:

"dependencies": {
    "@tailwindcss/typography": "^0.5.9",
    "@tippyjs/react": "^4.2.6",
    "@tiptap/extension-image": "^2.0.3",
    "@tiptap/extension-link": "^2.0.3",
    "@tiptap/extension-placeholder": "^2.0.3",
    "@tiptap/extension-underline": "^2.0.3",
    "@tiptap/extension-youtube": "^2.0.3",
    "@tiptap/pm": "^2.0.3",
    "@tiptap/react": "^2.0.3",
    "@tiptap/starter-kit": "^2.0.3",
    "@types/dateformat": "^5.0.0",
    "@types/formidable": "^2.0.6",
    "axios": "^1.4.0",
    "classnames": "^2.3.2",
    "cloudinary": "^1.36.4",
    "dateformat": "^5.0.3",
    "formidable": "^2.1.1",
    "html-react-parser": "^4.0.0",
    "joi": "^17.9.2",
    "mongoose": "^7.2.0",
    "next": "13.4.0",
    "next-auth": "^4.22.1",
    "nprogress": "^0.2.0",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-icons": "^4.8.0",
    "react-infinite-scroll-component": "^6.1.0",
    "slugify": "^1.6.6"
  },
  "devDependencies": {
    "@types/node": "20.0.0",
    "@types/nprogress": "^0.2.0",
    "@types/react": "18.2.5",
    "@types/react-dom": "18.2.3",
    "autoprefixer": "10.4.14",
    "eslint": "8.39.0",
    "eslint-config-next": "13.4.0",
    "postcss": "8.4.23",
    "tailwindcss": "3.3.2",
    "typescript": "5.0.4"
  }

我需要帮助来管理将应用程序构建到Vercel中

iezvtpos

iezvtpos1#

错误消息Cannot read properties of null (reading useState)通常发生在您尝试从React使用useState钩子时,但您作为参数提供给useState的值是nullundefined
在您提供的代码中,似乎没有为seoInitialValue设置默认值。您是否尝试将其设置为默认值?

相关问题