The post Populate data from mongoDB into Lexical Editor to edit for Blog Post appeared first on LoginVast.Com.
I am trying to show my data inside the Lexical Editor in a Next.js app and then submit the edited data. However I continue to get TypeError: Cannot read properties of undefined (reading ‘type’) when trying to JSON.stringifiy the data but nothing seems to work. When I console log the value it does show the actual data.
Editor.js
import { useEffect, useState } from "react";
/* Lexical Design System */
import { HeadingNode, QuoteNode } from "@lexical/rich-text";
import { TableCellNode, TableNode, TableRowNode } from "@lexical/table";
import { ListItemNode, ListNode } from "@lexical/list";
import { CodeHighlightNode, CodeNode } from "@lexical/code";
import { AutoLinkNode, LinkNode } from "@lexical/link";
import { TRANSFORMERS } from "@lexical/markdown";
/* Lexical Plugins Local */
import ToolbarPlugin from "@/utils/plugins/ToolbarPlugin";
import AutoLinkPlugin from "@/utils/plugins/AutoLinkPlugin";
import CodeHighlightPlugin from "@/utils/plugins/CodeHighlightPlugin";
import OnChangePlugin from "@/utils/plugins/onChangePlugin";
/* Lexical Plugins Remote */
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
import { AutoFocusPlugin } from "@lexical/react/LexicalAutoFocusPlugin";
import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
import { ListPlugin } from "@lexical/react/LexicalListPlugin";
import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin";
import { TabIndentationPlugin } from "@lexical/react/LexicalTabIndentationPlugin";
/* Lexical Others */
import { LexicalComposer } from "@lexical/react/LexicalComposer";
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
import LexicalErrorBoundary from "@lexical/react/LexicalErrorBoundary";
import ExampleTheme from "@/utils/theme/EditorTheme";
function Placeholder() {
return <div className="editor-placeholder">Enter some rich text...</div>;
}
const Editor = ({onChange, value}) => {
const [isMounted, setIsMounted] = useState(false)
const [floatingAnchorElem, setFloatingAnchorElem] = useState(null)
const onRef = (_floatingAnchorElem) => {
if (floatingAnchorElem !== null) {
setFloatingAnchorElem(floatingAnchorElem)
}
}
const editorConfig = {
namespace: 'RichTextEditor',
theme: ExampleTheme,
onError(error) {
throw error;
},
...(value && {
editorState: typeof value !== '' ? JSON.stringify(value) : value,
}),
nodes: [
HeadingNode,
ListNode,
ListItemNode,
QuoteNode,
CodeNode,
CodeHighlightNode,
TableNode,
TableCellNode,
TableRowNode,
AutoLinkNode,
LinkNode
],
};
useEffect(() => {
setIsMounted(true);
}, [])
if (!isMounted) return null
return (
<LexicalComposer initialConfig={editorConfig}>
<div className="editor-container">
<ToolbarPlugin />
<div className="editor-inner" ref={onRef}>
<RichTextPlugin
contentEditable={<ContentEditable className="editor-input" />}
placeholder={<Placeholder />}
ErrorBoundary={LexicalErrorBoundary}
/>
<ListPlugin />
<HistoryPlugin />
<AutoFocusPlugin />
<CodeHighlightPlugin />
<LinkPlugin />
<TabIndentationPlugin />
<AutoLinkPlugin />
<MarkdownShortcutPlugin transformers={TRANSFORMERS} />
{floatingAnchorElem && (
<FloatingLinkEditorPlugin anchorElem={floatingAnchorElem} />
)}
<OnChangePlugin onChange={onChange} />
</div>
</div>
</LexicalComposer>
)
}
export default Editor;
OnChangePlugin.js
import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import {useEffect} from 'react';
import { $generateHtmlFromNodes } from "@lexical/html";
export default function OnChangePlugin({ onChange }) {
const [editor] = useLexicalComposerContext();
useEffect(() => {
return editor.registerUpdateListener(({editorState}) => {
editor.update(() => {
const htmlString = $generateHtmlFromNodes(editor, null);
onChange({ content: htmlString, editorState })
});
});
}, [editor, onChange]);
}
BlogEdit.js
const BlogEdit = ({ params }) => {
const blogId = params.id;
const [{ loading, error, loadingUpdate, loadingUpload }, dispatch] =
useReducer(reducer, {
loading: true,
error: '',
});
const {
register,
control,
handleSubmit,
formState: { errors },
setValue,
} = useForm();
const editorRef = useRef();
const [isArticle, setIsArticle] = useState('');
const [isPublished, setIsPublished] = useState(false);
const [isImageOne, setIsImageOne] = useState('');
useEffect(() => {
const fetchData = async () => {
try {
dispatch({ type: 'FETCH_REQUEST' });
const { data } = await axios.get(`/api/blog`);
dispatch({ type: 'FETCH_SUCCESS' });
setValue('title', data.title);
setValue('slug', data.slug);
setValue('description', data.description);
setValue('article', data.article);
setIsArticle('article', data.article)
setValue('author', data.author);
setIsPublished(data.published);
setValue('headerImage', data.headerImage);
setIsImageOne(data.headerImage);
} catch (err) {
dispatch({ type: 'FETCH_FAIL', payload: getError(err) });
}
};
fetchData();
}, [blogId, setValue]);
const router = useRouter();
const submitHandler = async ({
title,
tags,
description,
author,
article,
published = isPublished,
headerImage,
slug
}) => {
try {
dispatch({ type: 'UPDATE_REQUEST' });
await axios.put(`/api/admin/blogs/${blogId}`, {
title,
tags,
description,
author,
article,
published,
headerImage,
slug
});
dispatch({ type: 'UPDATE_SUCCESS' });
toast.success('Blog updated successfully', {
theme: 'colored'
});
router.push('/admin/blogs');
} catch (err) {
dispatch({ type: 'UPDATE_FAIL', payload: getError(err) });
toast.error(getError(err), {
theme: 'colored'
});
}
};
return (
<Layout title="Admin Products">
<ToastContainer
position="top-center"
draggable={false}
transition={Slide}
autoClose={5000}
hideProgressBar={true}
className="toast-alert"
/>
<div className="admin-container page-contain bg-white py-5">
<Container fluid className="pt-5">
<Row>
<Col lg={2} className="mb-3">
<SideNav />
</Col>
<Col lg={10}>
<Card className="card admin-card-container p-4">
<Card.Body>
<Row className="gx-5">
{/* <Editor /> */}
<Card.Title className="text-center text-primary fs-1">{`Edit Category ${blogId}`}</Card.Title>
{loading ? (
<div className="spinner-border customer-spinner text-primary" role="status">
<span className="visually-hidden">Loading...</span>
</div>
) : error ? (
<div className="alert alert-danger" role="alert">
{error}
</div>
) : (
<form onSubmit={handleSubmit(submitHandler)} className="col-lg-12 col-md-12 col-sm-12 form-shipment justify-content-center">
<Row>
<Col lg={12}>
<div className="form-floating">
<input
type="text"
className="form-control"
id="title"
placeholder="Title"
{...register('title', {
required: 'Please enter blog title',
})}
/>
{errors.title && (
<div className="invalid-feedback">
{errors.title.message}
</div>
)}
<label htmlFor="title">Title</label>
</div>
<div className="form-floating">
<input
type="text"
className="form-control"
id="slug"
placeholder="Slug"
{...register('slug', {
required: 'Please enter slug',
})}
/>
{errors.slug && (
<div className="invalid-feedback">{errors.slug.message}</div>
)}
<label htmlFor="slug">Slug</label>
</div>
<div className="form-floating">
<input
type="text"
className="form-control"
id="author"
placeholder="Author"
{...register('author', {
required: 'Please enter an author',
})}
/>
{errors.author && (
<div className="invalid-feedback">{errors.author.message}</div>
)}
<label htmlFor="author">Author</label>
</div>
<div className="form-floating">
<input
type="text"
className="form-control"
id="description"
placeholder="Description"
{...register('description', {
required: 'Please enter an description',
})}
/>
{errors.author && (
<div className="invalid-feedback">{errors.description.message}</div>
)}
<label htmlFor="description">Description</label>
</div>
{isImageOne && ( <Image src={isImageOne} width={150} height={90} alt="Product Image One" /> )}
<div className="form-floating">
<input
type="text"
className="form-control"
id="headerImage"
placeholder="Blog Header Image"
{...register('headerImage', {
required: 'Please upload a blog header image',
})}
/>
{errors.headerImage && (
<div className="invalid-feedback">{errors.headerImage.message}</div>
)}
<label htmlFor="headerImage">Blog Header Image</label>
<div className="file btn btn-lg btn-primary">
Upload Image
<input
type="file"
id="imageFile"
/>
{loadingUpload && <Spinner as="span" animation="grow" size="sm" role="status" aria-hidden="true" />}
</div>
</div>
<div className="form-floating">
<Controller
control={control}
name="article"
render={({ field: fieldContent }) => (
<Controller
control={control}
name="article"
render={({ field }) => (
<Editor
onChange={value => {
field.onChange(value.editorState)
fieldContent.onChange(value.content)
}}
value={JSON.stringify(field.value)}
/>
)}
/>
)}
/>
</div>
<div className="form-floating">
<input
type="text"
className="form-control"
id="tags"
placeholder="Tags"
{...register('tags')}
/>
<label htmlFor="tags">Tags</label>
</div>
<div className="form-check my-3">
<input
className="form-check-input"
type="checkbox"
onChange={(e) => setIsPublished(e.target.checked)}
checked={isPublished}
name="published"
id="published"
/>
<label className="form-check-label" htmlFor="published">
Publish
</label>
</div>
</Col>
<button className="w-100 btn btn-lg btn-primary my-4" type="submit">
{loadingUpdate ? (
<Spinner
as="span"
animation="grow"
size="sm"
role="status"
aria-hidden="true"
/>
) : (
"Edit Blog Post"
)}
</button>
</Row>
</form>
)}
</Row>
</Card.Body>
</Card>
</Col>
</Row>
</Container>
</div>
</Layout>
)
}
I tried many different things to initialize the editorState in Lexical but no matter what I do I cannot get the data to show in the editor. I am able to add text and edit it which I also am able to submit to the database successfully. However, I need for the data to show in the editor and be able to edit and submit the updated version to the database.
The post Populate data from mongoDB into Lexical Editor to edit for Blog Post appeared first on LoginVast.Com.