codex-team / editor.js

A block-style editor with clean JSON output
https://editorjs.io
Apache License 2.0
28.4k stars 2.07k forks source link

Editor.js renders duplicate blocks when used in Svelte component #2314

Open TuzSeRik opened 1 year ago

TuzSeRik commented 1 year ago

Editor.js instance renders the same data from 1 to 3 times, when used with Svelte && SvelteKit.

Steps to reproduce:

  1. Create Editor.svelte component to house Editor.js instance

    
    <script>
    import { onMount } from "svelte";
    
    import EditorJS from '@editorjs/editorjs';
    import Header from '@editorjs/header';
    
    export let isReadOnly = true;
    export let data = {};
    
    let editor;
    async function startEditor() {
    editor = new EditorJS({
      holder: 'editorjs',
      readOnly: isReadOnly,
      data: data,
      tools: {
         header: Header,
      },
    });
    }
    
    onMount(startEditor);
    
    $: if (data && editor) {
    console.log("Editor - Update - Update: "
      + ("Updating editor with " + JSON.stringify(data)));
    
    editor.isReady.then(() => {
      editor.clear();
      editor.render(data);
    });
    }  
    </script>
2. Use this component in the outer component

3. Open the site either in dev mode or preview

Expected behaviour:
Data is always rendered once

Screenshots:

Data block duplicated
<img width="898" alt="image" src="https://user-images.githubusercontent.com/34165921/226594517-b650e0d1-456e-477d-998c-8104846c0a54.png">

Duplicated when only one fetch and one render called
<img width="956" alt="image" src="https://user-images.githubusercontent.com/34165921/226602842-fc81dc9c-ec3d-4165-95db-419ce629e3f2.png">

Device, Browser, OS:
Windows 11, Chrome 111.0.5563.65

Editor.js version:
2.26.5

Plugins you use with their versions:
Header - 2.7.0

Svelte and SvelteKit versions:
3.54.0 && 1.5.0
wiesson commented 1 year ago

What happens if you change it to

<script>
let editorRef = null;

...
holder: editorRef,
...
</script>

<div bind:this={editorRef} class="editor"></div>

? It works quite well for me with Sveltekit

Full example

```Svelte

```

TuzSeRik commented 1 year ago

What happens if you change it to

<script>
let editorRef = null;

...
holder: editorRef,
...
</script>

<div bind:this={editorRef} class="editor"></div>

? It works quite well for me with Sveltekit

Full example

Nothing happens really. The text still duplicates.

ddy-ddy commented 1 year ago

@TuzSeRik Thiserror occurs because there is no window object abailable during ssr, as the window object only exists in the browser. Therefore, when preforming ssr, we need to use dynamic import to delay loading Editor.js This code is work for me.

<script lang="ts">
  import { onMount } from 'svelte';

  let editor: any;

  onMount(async () => {
    const EditorJS = await import('@editorjs/editorjs');
    editor = new EditorJS.default({
      holder: 'editor',
      placeholder: 'Type something...',
      tools: {} // Add your tools here
    });
  });

</script>

<div id="editor"></div>
TuzSeRik commented 1 year ago

@TuzSeRik Thiserror occurs because there is no window object abailable during ssr, as the window object only exists in the browser. Therefore, when preforming ssr, we need to use dynamic import to delay loading Editor.js This code is work for me.

<script lang="ts">
  import { onMount } from 'svelte';

  let editor: any;

  onMount(async () => {
    const EditorJS = await import('@editorjs/editorjs');
    editor = new EditorJS.default({
      holder: 'editor',
      placeholder: 'Type something...',
      tools: {} // Add your tools here
    });
  });

</script>

<div id="editor"></div>

I'll try your solution, but I should notice, that I use adapter-static for SPA experience, not SSR

bouchtaoui-dev commented 1 year ago

@TuzSeRik Thiserror occurs because there is no window object abailable during ssr, as the window object only exists in the browser. Therefore, when preforming ssr, we need to use dynamic import to delay loading Editor.js This code is work for me.

<script lang="ts">
  import { onMount } from 'svelte';

  let editor: any;

  onMount(async () => {
    const EditorJS = await import('@editorjs/editorjs');
    editor = new EditorJS.default({
      holder: 'editor',
      placeholder: 'Type something...',
      tools: {} // Add your tools here
    });
  });

</script>

<div id="editor"></div>

I can confirm that your solution works well for me @ddy-ddy . I did almost the same, except I didn't know about the import() function 🙂

doubleyooz commented 11 months ago

the same happens in nuxt 3, it looks like the only way is to call destroy() and re-instantiate the editor.

uvinw commented 3 months ago

Thanks @ddy-ddy. Did you also manage to import editor-js/image using SvelteKit? I am posing here instead of in that repo because it seems to be abandoned. Following your approach:

<script lang="ts">
  import { onMount } from 'svelte';

  let editor: any;

  onMount(async () => {
    const EditorJS = await import('@editorjs/editorjs');
    const ImageTool = await import('@editorjs/image');

    editor = new EditorJS.default({
      holder: 'editor',
      placeholder: 'Type something...',
      tools: {
                image: {
                    class: ImageTool,
                    config: {
                        endpoints: {
                            byFile: '/api/uploadImage', // Your image upload endpoint
                            byUrl: '/api/fetchUrl', // Your image upload endpoint
                        }
                    }
                }
            }
    });
  });

</script>

This approach creates multiple errors on the browser console with the Editor not rendering.

image
marijuana-softwares-1 commented 1 month ago

i was plaing around it and it works fine to me

<script lang="ts">
  import { onMount } from 'svelte';

  let editor: any;

  onMount(async () => {
    const EditorJS = await import('@editorjs/editorjs');
    const Header = (await import('@editorjs/editorjs')).default;  // this way it will works (from taxonomy shadcn)
    editor = new EditorJS.default({
      holder: 'editor',
      placeholder: 'Type something...',
      tools: {
        header: Header,
      }
    });
  });

</script>

<div id="editor"></div>