'With Lexical, how do I set default initial text?
With the Lexical text editor framework, what's the easiest way to initialize the editor in React with a default text string?
I could, for instance, create an instance, manually save the JSON state, and then copy that JSON blob to my code, to set as initialEditorState
to PlainTextPlugin
, but this seems like I have to be missing something.
Thanks 🙏
Solution 1:[1]
Your intuition is correct. Avoid touching the EditorState directly, even when it's serialized as a JSON. Manipulating internals (inc. node private properties) can potentially lead to unexpected behavior/errors in future releases.
The initialEditorState
can take many shapes:
export type InitialEditorStateType = null | string | EditorState | (() => void);
null
-> empty EditorState (just a RootNode)string
-> a JSON stringified EditorState. Behind the scenes it callsJSON.parse(editor.setEditorState)
EditorState
-> an EditorState. Behind the scenes it calls -editor.setEditorState()
(the undo-redo/History plugin uses this)(() => void)
-> aneditor.update
function
The one you're interested in is (() => void)
-> an editor update.
You can run an editor.update
like follows:
<LexicalPlainTextPlugin initialEditorState={() => {
const paragraph = $createParagraphNode();
const text = $createTextNode('foo');
paragraph.append(text);
$getRoot().append(paragraph);
root.selectEnd();
}} />
No need to cache (useCallback) initialEditorState as it's only processed once
Side note: we're planning to move initialEditorState
(that currently lives in LexicalPlainTextPlugin
and LexicalRichTextPlugin
) to LexicalComposer
but it will work the same way.
We recommend avoiding manually handcrafted solutions too:
// PrepopulatePlugin.js
useLayoutEffect(() => {
editor.update(() => {
// Prepopulate
});
}, [editor]);
We built LexicalContentEditable to work well with SSR and handle the contenteditable
appropriately. If you were to build your own custom solution you would have to repeat this process.
Solution 2:[2]
The way to achieve it found in lexical codebase is like so
//...
<RichTextPlugin
initialEditorState={ prepopulatedRichText}/>
//...
function prepopulatedRichText() {
const root = $getRoot();
if (root.getFirstChild() === null) {
const heading = $createHeadingNode('h1');
heading.append($createTextNode('Welcome to the playground'));
root.append(heading);
const quote = $createQuoteNode();
quote.append(
$createTextNode(
`In case you were wondering what the black box at the bottom is – it's the debug view, showing the current state of editor. ` +
`You can hide it by pressing on the settings control in the bottom-right of your screen and toggling the debug view setting.`,
),
);
root.append(quote);
const paragraph = $createParagraphNode();
paragraph.append(
$createTextNode('The playground is a demo environment built with '),
$createTextNode('@lexical/react').toggleFormat('code'),
$createTextNode('.'),
$createTextNode(' Try typing in '),
$createTextNode('some text').toggleFormat('bold'),
$createTextNode(' with '),
$createTextNode('different').toggleFormat('italic'),
$createTextNode(' formats.'),
);
root.append(paragraph);
const paragraph2 = $createParagraphNode();
paragraph2.append(
$createTextNode(
'Make sure to check out the various plugins in the toolbar. You can also use #hashtags or @-mentions too!',
),
);
root.append(paragraph2);
const paragraph3 = $createParagraphNode();
paragraph3.append(
$createTextNode(`If you'd like to find out more about Lexical, you can:`),
);
root.append(paragraph3);
const list = $createListNode('bullet');
list.append(
$createListItemNode().append(
$createTextNode(`Visit the `),
$createLinkNode('https://lexical.dev/').append(
$createTextNode('Lexical website'),
),
$createTextNode(` for documentation and more information.`),
),
$createListItemNode().append(
$createTextNode(`Check out the code on our `),
$createLinkNode('https://github.com/facebook/lexical').append(
$createTextNode('GitHub repository'),
),
$createTextNode(`.`),
),
$createListItemNode().append(
$createTextNode(`Playground code can be found `),
$createLinkNode(
'https://github.com/facebook/lexical/tree/main/packages/lexical-playground',
).append($createTextNode('here')),
$createTextNode(`.`),
),
$createListItemNode().append(
$createTextNode(`Join our `),
$createLinkNode('https://discord.com/invite/KmG4wQnnD9').append(
$createTextNode('Discord Server'),
),
$createTextNode(` and chat with the team.`),
),
);
root.append(list);
const paragraph4 = $createParagraphNode();
paragraph4.append(
$createTextNode(
`Lastly, we're constantly adding cool new features to this playground. So make sure you check back here when you next get a chance :).`,
),
);
root.append(paragraph4);
}
}
but I prefer to use stringified editorState then parse it to editorState by parseEditorState function owned by instance.
Here is another problem I'm confused which also seems I have to be missed something.
ParseEditorState is a function only used for instance, but I can't use useLexicalComposerContext under the init component which will return LexicalComposer(unexpected error will cause), so I need to write an extra plugin to achieve it. Something like that:
import { useSelector } from "react-redux";
import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
import { useEffect } from "react";
export default function EditorStatePlugin() {
const activeNote = useSelector((state) => state.note.activeNote);
const [editor] = useLexicalComposerContext();
const state = editor.parseEditorState(
activeNote?.content ||
'{"_nodeMap":[["root",{"__children":["1"],"__dir":null,"__format":0,"__indent":0,"__key":"root","__parent":null,"__type":"root"}],["1",{"__type":"paragraph","__parent":"root","__key":"1","__children":[],"__format":0,"__indent":0,"__dir":null}]],"_selection":{"anchor":{"key":"1","offset":0,"type":"element"},"focus":{"key":"1","offset":0,"type":"element"},"type":"range"}}'
);
useEffect(() => {
editor.setEditorState(state);
}, [activeNote]);
return null;
}
It seems not a good way to write, is there a better way to do it?
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Josie |