-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Editor State from Headless Lexical and React Lexical slightly differs #7968
Description
Hi!
There is a lot of context in the issue referenced below, but to be short: it seems that extracting the Editor State from a headless or a React Lexical differs slightly (as shown in the context below).
This causes a few problems when trying to use the library with form libraries and to calculate dirty states. One of the fix is to manually manipulate the Editor State to remove the flaky properties, but it's risky as I'm touching Lexical internal properties with no guarantees that the system won't break in the future.
Is it possible to make sure that for the same content, the 2 Lexical editors can output the same Editor States?
Hi guys
We are facing a gnarly problem with our implementation of Lexical and React Hook Form.
Introduction
We have had normal inputs with html inside, and we are trying to implement Lexical to replace them. In our DB I store the html string, then the frontend fetches this html string, uses a headless Lexical to convert the html to a Lexical Editor State, and then use that calculated Editor State to initialize our Form, and by consequence Lexical as well. This is of course an adapter logic just for the migration; as soon as the user saves the Lexical again, we'll store directly the json stringified Editor State and not the html string as we used to have before.
This is the htmlToEditorStateHeadless function by the way, using the same nodes and config as our React Lexical editor:
/**
* Convert an HTML string into a stringified Lexical EditorState using a headless editor.
* Returns an empty string if the produced state has no text content.
*/
export function htmlToEditorStateHeadless(html: string, options: Options = {}): string {
const content = html.trim?.();
if (!content) return '';
const { nodes = NODES, namespace = NAMESPACE } = options;
try {
const editor = createHeadlessEditor({ namespace, onError, nodes });
editor.update(
() => {
const parser = new DOMParser();
const dom = parser.parseFromString(content, 'text/html');
const nodesFromDom = $generateNodesFromDOM(editor, dom);
$getRoot().clear();
$getRoot().select();
$insertNodes(nodesFromDom);
},
{
discrete: true,
},
);
const editorState = editor.getEditorState();
let isEmpty = false;
editorState.read(() => {
isEmpty = $isRootTextContentEmpty(false, true);
});
if (isEmpty) return '';
return JSON.stringify(editorState);
} catch (err) {
onError(err);
return '';
}
}Problem
The problem we are facing is that, as soon as the user touches the Lexical editor, the RHF goes dirty. I checked the diff between the initialization Lexical state and the new one after touching it, and it seems that the difference appears here
There is textFormat: 3 that appears twice in the modified version. This only happens on html strings that have some kind of rich texts inside, like bold, italic, etc..
Improvement attempt
First I was wondering why just the mere clicking on the Lexical Editor would trigger a change, then I found out that the OnChange plugin has a property called ignoreSelectionChange. This fixed the fact that the change would happen on merely clicking in the editor but it didn't fix the underlying problem.
In fact, now if I add a letter to the Editor and delete it right after, the underlying Editor State results modified and, by consequence, the form is dirty. In this case, I'd expect the state to be equal to initialization, which is not.
Any suggestions on what could cause the problem, or what could we do better to avoid this problem or achieve our migration? Thank you 🙏
Originally posted by @buondevid in #7965