我试图将我的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中
1条答案
按热度按时间iezvtpos1#
错误消息
Cannot read properties of null (reading useState)
通常发生在您尝试从React使用useState钩子时,但您作为参数提供给useState的值是null或undefined。在您提供的代码中,似乎没有为
seoInitialValue
设置默认值。您是否尝试将其设置为默认值?