3

Im trying to deploy my NextJs(V13) to Vercel but it gives me this error: image (https://i.stack.imgur.com/iLZSA.png)

It seems the issue is comming from useEditor which is for @tiptap/react library this is my create page:

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;

and this is my Editor component:

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;

as for my 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"
  }

I need help to manage to build the app into the Vercel

1 Answers1

0

The error message Cannot read properties of null (reading useState) typically occurs when you're trying to use the useState hook from React, but the value you provided as an argument to useState is null or undefined.

In the code you provided seems that you don't have set default values for seoInitialValue. Did you try to set the default value to it?

Saba Shavidze
  • 604
  • 4
  • 14
  • I even commented all the states I had within that component and the error is still there. If you have a close look on error, it states that its coming from useEditor hook which is correct cause once i comment it the error will be gone – mahdi zangeneh Jun 05 '23 at 03:49