Search code examples
frontendreact-typescripteditorjs

editorInstance.destroy is not function?


I am creating a Blog website for which i require a text-editor. So i decided to use editorjs for my project because i could understand their docs. But i am facing a problem, whenever i run the below component(Editor), it gives an error: editorInstance.destroy is not a function(the details of the error are described below the code-section) In their docs they have mentioned, on how to remove an editorjs instance:

/**
* Create an instance
*/
const editor = new EditorJS();

/**
* Destroy editor.
*/
editor.destroy();

editor2.ts

// Import necessary modules
import React, { useRef,useEffect } from "react";
import EditorJS, { OutputData } from "@editorjs/editorjs";
import {Tools} from "./Tools";
import "./styles.css";

interface EditorProps {
  initialData?: OutputData; // Optional initial data in JSON format
}

// Define the Editor component
const Editor: React.FC<EditorProps> = ({ initialData }) => {

  // Function to handle saving the editor content
  const handleSave = async () => {
    console.log("saving content...");
    //implement logic
  };

 
  // Initialize the editor when the component mounts
  useEffect(() => {
    // Initialize EditorJS
      let editorInstance = new EditorJS({
        holder: "editorjs",
        tools: Tools
      });
    
    editorInstance.isReady
    .then(()=>console.log("Editor.js is ready to work!"))
    .catch((err)=>(console.error(err)));
    
    // Cleanup function to destroy the editor when the component unmounts
    return () => {
      if(editorInstance){
        console.log("destroying editor instance...");
        editorInstance.destroy();
      }
    }
    
  }, [initialData]);

  return (
    <div className="editor-container">
      <div id="editorjs" style={{ minHeight: "300px" }}></div>
      <button className="save-button" onClick={handleSave}>
        Save
      </button>
    </div>
  );
};

export default Editor;

Error:

react-dom_client.js?v=f02bae0d:8786 Uncaught TypeError: editorInstance.destroy is not a function
    at Editor2.tsx:37:24
    at safelyCallDestroy (react-dom_client.js?v=f02bae0d:16296:13)
    at commitHookEffectListUnmount (react-dom_client.js?v=f02bae0d:16423:19)
    at commitPassiveUnmountInsideDeletedTreeOnFiber (react-dom_client.js?v=f02bae0d:17831:17)
    at commitPassiveUnmountEffectsInsideOfDeletedTree_begin (react-dom_client.js?v=f02bae0d:17793:13)
    at commitPassiveUnmountEffects_begin (react-dom_client.js?v=f02bae0d:17729:19)
    at commitPassiveUnmountEffects (react-dom_client.js?v=f02bae0d:17717:11)
    at flushPassiveEffectsImpl (react-dom_client.js?v=f02bae0d:19009:11)
    at flushPassiveEffects (react-dom_client.js?v=f02bae0d:18967:22)
    at react-dom_client.js?v=f02bae0d:18848:17

my package.json file:

{
  "name": "react-typescript",
  "version": "1.0.0",
  "type": "module",
  "description": "React TypeScript on Replit, using Vite bundler",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/react": "^18.0.15",
    "@types/react-dom": "^18.0.6",
    "@vitejs/plugin-react": "^2.0.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "typescript": "^4.7.4",
    "vite": "^3.0.4"
  },
  "dependencies": {
    "@editorjs/checklist": "^1.6.0",
    "@editorjs/code": "^2.9.0",
    "@editorjs/delimiter": "^1.4.2",
    "@editorjs/editorjs": "^2.30.5",
    "@editorjs/embed": "^2.7.4",
    "@editorjs/header": "^2.8.7",
    "@editorjs/image": "^2.9.3",
    "@editorjs/inline-code": "^1.5.1",
    "@editorjs/link": "^2.6.2",
    "@editorjs/list": "^1.10.0",
    "@editorjs/marker": "^1.4.0",
    "@editorjs/nested-list": "^1.4.3",
    "@editorjs/paragraph": "^2.11.6",
    "@editorjs/quote": "^2.7.2",
    "@editorjs/raw": "^2.5.0",
    "@editorjs/table": "^2.4.1",
    "@editorjs/underline": "^1.1.0",
    "@editorjs/warning": "^1.4.0"
  }
}

I asked co-pilot, it just told either to make the variable accessible within the entire component(which didn't work) and the problem is caused because the DOM is not ready(i didn't understand this) I also searched for the implementation and got a online codesandbox.io link, which did same thing && also created a ref to hold EditorJS instance which works(i didn't test, just saw it in their preview) Even if i am able to run solve it by using ref, why is my way not working, why we need to do the above?


Solution

  • I think the editorInstance is not properly scoped or referenced when you call destroy(). In your current code, editorInstance is a local variable within the useEffect hook. The teardown is executing an anonymous function which means that the variable is out of scope and that is why you get the null ref (I don't have the full context but that is my best guess ¯_(ツ)_/¯).

    I would use a reference to store the instance so it remains the same across the multiple renders. The reason it's rendering twice is because React runs lifecycle functions like useEffect twice when in Strict Mode (which is enabled by default in development environments)

    This component only renders the editor once and only destroys it if it has the destroy function (which it will only have if it is fully initialized)

    // All imports ...
    
    const Editor: React.FC<EditorProps> = ({ initialData }) => {
      const editorRef = useRef<HTMLDivElement>(null)
      const editorInstance = useRef<EditorJS | null>(null);
      // rest of the code
    
    useEffect(() => {
        if (!editorRef.current || editorInstance.current) {
          return
        }
    
        const editor = new EditorJS({
            holder: editorRef.current,
            tools: Tools
          });
    
        editorInstance.current = editor;
    
        return () => {
          if (editorInstance.current) {
            editorInstance.current.destroy();
            editorInstance.current = null;
          }
        };
      }, [editorRef.current]);
    
    // rest of the code
    
    

    Hope it gives you some insight on how to solve the issue