Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug: registerUpdateListener fires the function once more when the editor mounts or unmounts #7198

Open
YugyeomKim opened this issue Feb 18, 2025 · 5 comments

Comments

@YugyeomKim
Copy link

YugyeomKim commented Feb 18, 2025

Lexical version: 0.24.0

Steps To Reproduce

Add an registerUpdateListener of an editor object from useLexicalComposerContext in a useEffect hook.

Code example:

import type { SerializedEditorState } from 'lexical';

import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
import debounce from 'lodash.debounce';
import { useCallback, useEffect } from 'react';

type UpdatePluginProps = {
  onChange: (editorState: null | SerializedEditorState) => void;
};

export default function UpdatePlugin({ onChange }: UpdatePluginProps) {
  const [lexicalEditor] = useLexicalComposerContext();

  // eslint-disable-next-line react-hooks/exhaustive-deps
  const saveData = useCallback(
    debounce(() => {
            // Do something here like saving the contents to db
      });
    }, 1000),
    [lexicalEditor],
  );

  useEffect(() => {
    const unregisterListener = lexicalEditor.registerUpdateListener(saveData);

    return () => {
      unregisterListener();
    };
  }, [lexicalEditor, saveData]);

  return null;
}

The current behavior

After updating lexical version from 0.18.0 to 0.24.0, registerUpdateListener fires the function passed as a parameter whenever the editor mounts or unmounts.
This execution of the function happens before the cleaner of the useEffect runs, making it hard to handle the execution properly.

The expected behavior

  • Option 1: The listener function doesn't run whenever the editor mounts or unmounts.
  • Option 2: There is an option for registerUpdateListener to disable this functionality

Impact of fix

We are using an index-based approach to perform CRUD operations on an editor (in reality the parent of the editor).
When an editor is removed from the array, the index shifts and points to a different editor.
Since we are using lodash.debounce inside the update listener function, the debounced update from the removed editor ends up overwriting the data of a newly added editor in the DOM.
We are unable to selectively cancel the debounced function when the editor unmounts.

@etrepum
Copy link
Collaborator

etrepum commented Feb 18, 2025

Why don’t you include a reference to the editor in your callback and ignore it if it doesn’t match the editor in your array? Using React’s key property correctly might also fix this for you

@etrepum
Copy link
Collaborator

etrepum commented Feb 18, 2025

You can also have a ref that tracks if the editor is unmounted and return from that debounced function body when that has happened.

@YugyeomKim
Copy link
Author

@etrepum

Thanks for the suggestions!
Unfortunately, they don’t quite fit our case.

We want to keep the debounced function whose timer has been started when the editor is unmounted, so that any changes made right before the editor’s removed get saved to our DB.
It seems like your approach would prevent that from happening.

That said, we’re still having issues with the debounced function running as the editor is being unmounted—it’s causing problems in our app.

@YugyeomKim
Copy link
Author

Using React’s key property correctly might also fix this for you

I've tried assigning a unique key for each editor, but it didn't work.

@etrepum
Copy link
Collaborator

etrepum commented Feb 18, 2025

Certainly passing the key through to your callback so you can verify that the update came from the editor you expected to should work.

That said, if you can provide a runnable reproduction of this issue (e.g on stackblitz or codesandbox) I can look into it further.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants