Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EditorJS in NextJS not able to load plugins

I am trying to get EditorJS working in NextJS. The editor loads fine without plugins, having the only paragraph as a block option. However, when I attempt to add plugins via tools prop console throws the following warning:

editor.js?9336:2 Module Tools was skipped because of TypeError: Cannot read property 'prepare' of undefined

When I click on the editor in the browser, it is throwing:

Uncaught TypeError: Cannot read property 'holder' of undefined

I have tested editor plugins in the normal React app, and they load fine. Meaning that the problem is in EditorJS and NextJS import and handling of plugins. I have tried to import editor and plugins in componentDidMount hook using require but had the same problem as with NextJS dynamic imports. Attempted to get component using React ref but found that currently NextJS has problems with getting components' refs, Tried suggested workaround but still had no result. The instance of the editor is not available until onChange is triggered, so plugins just cannot hook into the editor due to that 'prepare' property or the whole editor are being undefined until an event on editor has happened, but the editor outputs into the console that it is ready.

My component's code:

import React from "react";
import dynamic from "next/dynamic";

const EditorNoSSR = dynamic(() => import("react-editor-js"), { ssr: false });
const Embed = dynamic(() => import("@editorjs/embed"), { ssr: false });
class Editor extends React.Component {
  state = {
    editorContent: {
      blocks: [
        {
          data: {
            text: "Test text",
          },
          type: "paragraph",
        },
      ],
    },
  };

  constructor(props) {
    super(props);
    this.editorRef = React.createRef();
  }

  componentDidMount() {
    console.log(this.editorRef.current);
    console.log(this.editorInstance);
  }

  onEdit(api, newData) {
    console.log(this.editorRef.current);
    console.log(this.editorInstance);

    this.setState({ editorContent: newData });
 }

  render() {
    return (
      <EditorNoSSR
        data={this.state.editorContent}
        onChange={(api, newData) => this.onEdit(api, newData)}
        tools={{ embed: Embed }}
        ref={(el) => {
          this.editorRef = el;
        }}
        instanceRef={(instance) => (this.editorInstance = instance)}
      />
    );
  }
}

export default Editor;

Is there any solution to this problem? I know SSR is challenging with client side rendering of components that access DOM, but there was condition used that checked whether window object is undefined, however, it does not look like an issue in my situation.

UPDATE:

I have found a solution but it is rather not a NextJS way of solving the problem, however, it works. It does not require a react-editorjs and implemented as creation of EditorJS instance as with normal EditorJS.

class Editor extends React.Component {
 constructor(props) {
   super(props);
   this.editor = null;
 }

 async componentDidMount() {
   this.initEditor();
 }

 initEditor = () => {
   const EditorJS = require("@editorjs/editorjs");
   const Header = require("@editorjs/header");
   const Embed = require("@editorjs/embed");
   const Delimiter = require("@editorjs/delimiter");
   const List = require("@editorjs/list");
   const InlineCode = require("@editorjs/inline-code");
   const Table = require("@editorjs/table");
   const Quote = require("@editorjs/quote");
   const Code = require("@editorjs/code");
   const Marker = require("@editorjs/marker");
   const Checklist = require("@editorjs/checklist");

   let content = null;
   if (this.props.data !== undefined) {
     content = this.props.data;
   }

   this.editor = new EditorJS({
     holder: "editorjs",
     logLevel: "ERROR",
     tools: {
       header: Header,
       embed: {
         class: Embed,
         config: {
           services: {
             youtube: true,
             coub: true,
           },
         },
       },
       list: List,
       inlineCode: InlineCode,
       code: Code,
       table: Table,
       quote: Quote,
       marker: Marker,
       checkList: Checklist,
       delimiter: Delimiter,
     },

     data: content,
   });
 };
 async onSave(e) {
   let data = await this.editor.saver.save();

   this.props.save(data);
 }

 render() {
   return (
     <>
       <button onClick={(e) => this.onSave(e)}>Save</button>
       <div id={"editorjs"} onChange={(e) => this.onChange(e)}></div>
     </>
   );
 }
}

This implementation works in NextJS

I will update code if I find a better solution.

UPDATE 2:

The answer suggested by Rising Odegua is working.

like image 286
Roman Burdun Avatar asked Jun 24 '20 20:06

Roman Burdun


1 Answers

You have to create a seperate component and then import all your tools there:

import EditorJs from "react-editor-js";
import Embed from "@editorjs/embed";
import Table from "@editorjs/table";
import List from "@editorjs/list";
import Warning from "@editorjs/warning";
import Code from "@editorjs/code";
import LinkTool from "@editorjs/link";
import Image from "@editorjs/image";
import Raw from "@editorjs/raw";
import Header from "@editorjs/header";
import Quote from "@editorjs/quote";
import Marker from "@editorjs/marker";
import CheckList from "@editorjs/checklist";
import Delimiter from "@editorjs/delimiter";
import InlineCode from "@editorjs/inline-code";
import SimpleImage from "@editorjs/simple-image";

const CustomEditor = () => {

    const EDITOR_JS_TOOLS = {
        embed: Embed,
        table: Table,
        marker: Marker,
        list: List,
        warning: Warning,
        code: Code,
        linkTool: LinkTool,
        image: Image,
        raw: Raw,
        header: Header,
        quote: Quote,
        checklist: CheckList,
        delimiter: Delimiter,
        inlineCode: InlineCode,
        simpleImage: SimpleImage
    };

    return (

        <EditorJs tools={EDITOR_JS_TOOLS} />

    );
}

export default CustomEditor;

Then in your NextJS page, use a dynamic import like this:

let CustomEditor;
if (typeof window !== "undefined") {
  CustomEditor = dynamic(() => import('../src/components/CustomEditor'));
}

And you can use your component:

return (
  {CustomEditor && <CustomEditor />}
)

Source : https://github.com/Jungwoo-An/react-editor-js/issues/31

like image 55
Rising Odegua Avatar answered Oct 14 '22 12:10

Rising Odegua