From cc2522e587a8ee0f3520d19fdd9995bc76161804 Mon Sep 17 00:00:00 2001 From: Acy Watson Date: Mon, 31 Jul 2023 07:33:24 -0600 Subject: [PATCH] Expand react docs (#4845) --- .../docs/getting-started/react.md | 105 +++++++++++++++--- 1 file changed, 92 insertions(+), 13 deletions(-) diff --git a/packages/lexical-website/docs/getting-started/react.md b/packages/lexical-website/docs/getting-started/react.md index dc32b8d6263..7473f0e55cd 100644 --- a/packages/lexical-website/docs/getting-started/react.md +++ b/packages/lexical-website/docs/getting-started/react.md @@ -43,18 +43,6 @@ const theme = { ... } -// When the editor changes, you can get notified via the -// LexicalOnChangePlugin! -function onChange(editorState) { - editorState.read(() => { - // Read the contents of the EditorState here. - const root = $getRoot(); - const selection = $getSelection(); - - console.log(root, selection); - }); -} - // Lexical React plugins are React components, which makes them // highly composable. Furthermore, you can lazy load plugins if // desired, so you don't pay the cost for plugins until you @@ -91,10 +79,101 @@ function Editor() { placeholder={
Enter some text...
} ErrorBoundary={LexicalErrorBoundary} /> - ); } ``` + +Now that we have a simple editor in React, the next thing we might want to do is access the content of the editor to, for instance, +save it in a database. We can do this via the an [update listener](https://lexical.dev/docs/concepts/listeners#registerupdatelistener), which will execute every time the editor state changes and provide us with the latest state. In React, we typically use the plugin system to set up listeners like this, since it provides us easy access to the LexicalEditor instance via a React Context. So, let's write our own plugin that notifies us when the editor updates. + +```jsx +// When the editor changes, you can get notified via the +// OnChangePlugin! +function OnChangePlugin({ onChange }) { + // Access the editor through the LexicalComposerContext + const [editor] = useLexicalComposerContext(); + // Wrap our listener in useEffect to handle the teardown and avoid stale references. + useEffect(() => { + // most listeners return a teardown function that can be called to clean them up. + return editor.registerUpdateListener((editorState) => { + // call onChange here to pass the latest state up to the parent. + onChange(editorState); + }); + }, [editor, onChange]); + +} +``` + +Now, we can implement this in our editor and save the EditorState in a React state variable: + +```jsx +function OnChangePlugin({ onChange }) { + const [editor] = useLexicalComposerContext(); + useEffect(() => { + return editor.registerUpdateListener((editorState) => { + onChange(editorState); + }); + }, [editor, onChange]); +} + +function Editor() { + // ... + + const [editorState, setEditorState] = useState(); + function onChange(editorState) { + setEditorState(editorState); + } + + return ( + + } + placeholder={
Enter some text...
} + ErrorBoundary={LexicalErrorBoundary} + /> + + + +
+ ); +} + +``` +Ok, so now we're saving the EditorState object in a React state variable, but we can't save a JavaScript object to our database - so how do we persist the state so we can load it later? We need to serialize it to a storage format. For this purpose (among others) Lexical provides several serialization APIs that convert EditorState to a string that can be sent over the network and saved to a database. Building on our previous example, we can do that this way: + +```jsx +function OnChangePlugin({ onChange }) { + const [editor] = useLexicalComposerContext(); + useEffect(() => { + return editor.registerUpdateListener((editorState) => { + onChange(editorState); + }); + }, [editor, onChange]); +} + +function Editor() { + // ... + + const [editorState, setEditorState] = useState(); + function onChange(editorState) { + // Call toJSON on the EditorState object, which produces a serialization safe string + const editorStateJSON = editorState.toJSON(); + // However, we still have a JavaScript object, so we need to convert it to an actual string with JSON.stringify + setEditorState(JSON.stringify(editorStateJSON)); + } + + return ( + + {/*...*/} + + + ); + +``` + +From there, it's straightforward to wire up a submit button or some other UI trigger that will take the state from the React state variable and send it to a server for storage in a database. + +One important thing to note: Lexical is generally meant to be uncontrolled, so avoid trying to pass the EditorState back into Editor.setEditorState or something along those lines.