editor.js icon indicating copy to clipboard operation
editor.js copied to clipboard

Render function not clearing existing blocks.

Open Nitwel opened this issue 2 years ago • 5 comments

When using the render function, it doesn't seem to clear old data but creates duplicate blocks.

https://editorjs.io/blocks/#render states: Method removes all Blocks and fills with new passed JSON data

Steps to reproduce: call the render function twice closely after each other

Expected behavior: only show default block once.

Screenshots: grafik

Editor.js version: latest

Nitwel avatar Aug 15 '23 10:08 Nitwel

see how i had impleamented it

my Editor.js code

"use client"; import React, { FC, useCallback, useEffect, useRef, useState } from "react"; import { useForm } from "react-hook-form"; import ReactTextareaAutosize from "react-textarea-autosize"; import { zodResolver } from "@hookform/resolvers/zod"; import EditorJS from "@editorjs/editorjs"; import { useMutation } from "@tanstack/react-query"; import axios from "axios"; import { useRouter } from "next/navigation"; import { handleImageUpload } from "@/lib/Functions"; import { PostCreationRequest, postValidator } from "./CreatePostValidator"; import { Loader2 } from "lucide-react"; import { exitPopup } from "./button/CreatePostExitPost";

interface editorProp { userId: string; }

const Editor: FC<editorProp> = ({ userId }) => { const router = useRouter();

const { register, handleSubmit, formState: { errors }, } = useForm<PostCreationRequest>({ resolver: zodResolver(postValidator), defaultValues: { userId, title: "", content: null, }, });

const ref = useRef<EditorJS>(); const [isMounted, setIsMounted] = useState(false); const _titleRef = useRef<HTMLTextAreaElement>(null); useEffect(() => { if (typeof window !== "undefined") { setIsMounted(true); } }, []);

// EDITOR INITALISATION FUNCTION --- > const initializeEditor = useCallback(async () => { const EditorJS = (await import("@editorjs/editorjs")).default; // @ts-ignore const Header = (await import("@editorjs/header")).default; // @ts-ignore const Embed = (await import("@editorjs/embed")).default; // @ts-ignore const Table = (await import("@editorjs/table")).default; // @ts-ignore const List = (await import("@editorjs/list")).default; // @ts-ignore const Code = (await import("@editorjs/code")).default; // @ts-ignore const LinkTool = (await import("@editorjs/link")).default; // @ts-ignore const InlineCode = (await import("@editorjs/inline-code")).default; // @ts-ignore const ImageTool = (await import("@editorjs/image")).default;

if (!ref.current) {
  const editor = new EditorJS({
    holder: "editor",
    onReady() {
      ref.current = editor;
    },
    placeholder: "Type here to write your post...",
    inlineToolbar: true,
    data: { blocks: [] },
    tools: {
      header: Header,
      linkTool: {
        class: LinkTool,
        config: {
          endpoint: "/api/link",
        },
      },
      image: {
        class: ImageTool,
        config: {
          uploader: {
            uploadByFile: handleImageUpload,
          },
        },
      },
      list: List,
      code: Code,
      inlineCode: InlineCode,
      embed: Embed,
      table: Table,
    },
  });
}

}, []);

// INITIALIZING useEffect(() => { const init = async () => { await initializeEditor();

  setTimeout(() => {
    _titleRef?.current?.focus();
  }, 0);
};

if (isMounted) {
  init();

  return () => {
    ref.current?.destroy();
    ref.current = undefined;
  };
}

}, [isMounted, initializeEditor]);

// ERROR HANDLING OF EDITOR useEffect(() => { if (Object.keys(errors).length) { for (const [_key, value] of Object.entries(errors)) { window.alert((value as { message: string }).message); } } }, [errors]);

const { mutate: createPost, isLoading } = useMutation({ mutationFn: async ({ title, content, userId }: PostCreationRequest) => { const payload: PostCreationRequest = { title, content, userId }; const { data } = await axios.post("/api/user/post/create", payload); return data; }, onError: () => { window.alert("Editor submit Error"); }, onSuccess: () => { // A WAY TO HANDLE CREATE POST AND REDIRECT IT router.refresh() ref.current?.blocks.clear(); window.alert("Successfully Posted"); exitPopup(); }, });

// EDITOR ON SUBMIT async function onSubmit(data: PostCreationRequest) { const blocks = await ref.current?.save();

const payload: PostCreationRequest = {
  title: data.title,
  content: blocks,
  userId,
};

createPost(payload);

}

if (!isMounted) { return null; } // CUSTOM REF SETTING const { ref: titleRef, ...rest } = register("title");

return ( <div className="w-full p-4 bg-zinc-50 rounded-lg border border-zinc-200 "> {/* On SUBMIT LOADING /} {isLoading && ( <div className="absolute h-fit w-fit m-auto top-0 bottom-0 left-0 right-0 z-50 flex justify-center items-center bg-zinc-50"> <Loader2 className="h-14 w-14 text-[#3d70b2] animate-spin" /> )} <form id="create_post" className="w-fit" onSubmit={handleSubmit(onSubmit)} > <div className="max-w-full"> {/ TEXTAREA FOR POST TITLE */} <ReactTextareaAutosize placeholder="Title" disabled={isLoading} ref={(e) => { titleRef(e); // @ts-ignore _titleRef.current = e; }} {...rest} className="w-full resize-none overflow-hidden bg-transparent appearance-none text-5xl font-bold focus:outline-none" /> <div id="editor" className="min-h-[50%]" /> ); };

export default Editor;

taqui-786 avatar Aug 15 '23 17:08 taqui-786

Also when you call new EditorJS on a div that has already some blocks in the HTML, it does not clear existing data.

The correct behavior should be clearing existing data inside the editor element.

collimarco avatar Dec 11 '23 12:12 collimarco

Did you find a solution to this problem with Editor.js using React?

obfusticatedcode avatar Feb 19 '24 08:02 obfusticatedcode

This is my solution to the problem.

==========================================================

import { useEffect, useState, useRef } from 'react' import EditorJS from '@editorjs/editorjs'

const Editor = ({ data }) => { const isMoundedRef = useRef(false) const [editorControl, setEditorControl] = useState<EditorJS>()

useEffect(() => { initEditor() }, [])

useEffect(() => { if (isMoundedRef.current && data) { initData() } }, [isMoundedRef.current, data])

// Initializing the Editor const initEditor = async () => { const editor = new EditorJS({ holder: 'customeow-editorjs', }) await editor.isReady isMoundedRef.current = true setEditorControl(editor) }

// Initialization data const initData = async () => { try { if (editorControl && data) { editorControl.render(data) } } catch (reason) {

}

}

return

}

export default Editor

W3LiangXN avatar Mar 07 '24 02:03 W3LiangXN