ckeditor / ckeditor5-react

Official CKEditor 5 React component.
https://ckeditor.com/ckeditor-5
Other
425 stars 99 forks source link

Add `onChangeInitializedEditors` callback to `CKEditorContext` #514

Closed Mati365 closed 3 months ago

Mati365 commented 3 months ago

Suggested merge commit message (convention)

Feature: Add an onChangeInitializedEditors callback to CKEditorContext to allow tracking of newly initialized editors within the JSX React tree. Closes https://github.com/ckeditor/ckeditor5-react/issues/513


Additional information

During the React 19 refactor, it was discovered that CKEditorContext must be initialized before rendering the rest of the React tree (due to StrictMode re-renders on CKEditorContext). This order is also necessary because the rendering of newer React JSX trees is asynchronous, and it is not guaranteed that all CKEditor instances will be registered in the context before onReady is called.

The issue is that some implementations rely on this behavior. Like this one: https://github.com/ckeditor/ckeditor5-react/issues/513

While we cannot track the full initialization of the entire tree, we can incrementally track what has been added.

⚠️ I'll update docs after approval of this PR

This PR adds:

  1. onChangeInitializedEditors callback to CKEditorContext component, that is fired after any initialization or destruction of any editor in the context.
  2. contextItemMetadata property to CKEditor component that allows onChangeEditorsMap to assign proper name to initialized / or destroyed editor.
Example
<CKEditorContext
  context={ ClassicEditor.Context }
  contextWatchdog={ ClassicEditor.ContextWatchdog }
  onChangeInitializedEditors={ editors => {
    console.log( editors );
  }}
>
  <CKEditor
    editor={ ClassicEditor }
    data="<h2>Editor</h2>"
    contextItemMetadata={{
      name: 'editor1',
      user: { id: '2' }
    }}
  />

  <CKEditor
    editor={ ClassicEditor }
    data="<h2>Another Editor</h2><p>... in common Context</p>"
    contextItemMetadata={{
      name: 'editor2'
    }}
  />
</CKEditorContext>

onWatchInitializedEditors will be called twice in example above:

  1. First log: { editor1: ... }
  2. Second log: { editor1: ..., editor2: ... }

Order of initialization is not guaranteed. editor2 might be initialized before editor1.

glynam1 commented 3 months ago

Hi,

Just a question on this implementation so I can confirm closure of https://github.com/ckeditor/ckeditor5-react/issues/513 when this is merged, onChangeEditorsMap will be fired when an editor is ready right? So this is equivalent to listening to the onReady callback from an editor.

If this is the same as the callback from context.editors.on<CollectionChangeEvent<Editor>>( 'change', ( ev, data ) => {

Then I think this won't be useful as the editor is still not actually ready.

Mati365 commented 3 months ago

@glynam1 It'll be fired when editor is ready (and after destroying of editor). It's the flat map of fully initialized editors that are present in React tree.

glynam1 commented 3 months ago

@glynam1 It'll be fired when editor is ready (and after destroying of editor). It's the flat map of fully initialized editors that are present in React tree.

Nice one, looking forward to the update so!

arkflpc commented 3 months ago

LGTM, but the naming is not clear enough.

Mati365 commented 3 months ago

@arkflpc I renamed callback to onWatchInitializedEditors. Can you take a look?