admin管理员组

文章数量:1401307

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 ponentDidMount hook using require but had the same problem as with NextJS dynamic imports. Attempted to get ponent using React ref but found that currently NextJS has problems with getting ponents' 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 ponent'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();
  }

  ponentDidMount() {
    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 ponents 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 ponentDidMount() {
   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.

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 ponentDidMount hook using require but had the same problem as with NextJS dynamic imports. Attempted to get ponent using React ref but found that currently NextJS has problems with getting ponents' 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 ponent'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();
  }

  ponentDidMount() {
    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 ponents 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 ponentDidMount() {
   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.

Share Improve this question edited Apr 6, 2021 at 12:52 Roman Burdun asked Jun 24, 2020 at 20:09 Roman BurdunRoman Burdun 1031 silver badge7 bronze badges 2
  • i never touched next.js, but working with nuxt.js. in nuxt.js you can check with process.client if you are on client side. Did you tried it in next.js with if(process.browser){ ... } – Ilijanovic Commented Jun 24, 2020 at 20:15
  • I have used condition if(typeof window !== 'undefined'), just to make sure it behaves as it should but that does not change anything because it suppose to render "from my understanding" dynamic imports with disabled ssr flag as client side only. – Roman Burdun Commented Jun 24, 2020 at 20:49
Add a ment  | 

1 Answer 1

Reset to default 5

You have to create a seperate ponent 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/ponents/CustomEditor'));
}

And you can use your ponent:

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

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

本文标签: javascriptEditorJS in NextJS not able to load pluginsStack Overflow