使用 Lexical,如何设置默认初始文本?

fis*_*ch2 18 lexicaljs

使用词法文本编辑器框架,在 React 中使用默认文本字符串初始化编辑器的最简单方法是什么?

例如,我可以创建一个实例,手动保存 JSON 状态,然后将该 JSON blob 复制到我的代码中,以设置为initialEditorStatePlainTextPlugin但这似乎我必须缺少一些东西。

谢谢

zur*_*fyx 13

你的直觉是正确的。避免直接接触 EditorState,即使它被序列化为 JSON。操纵内部结构(包括节点私有属性)可能会导致未来版本中出现意外的行为/错误。

initialEditorState采取多种形状:

export type InitialEditorStateType = null | string | EditorState | (() => void);

  • null-> 空 EditorState (只是一个 RootNode)
  • string-> JSON 字符串化的 EditorState。它在幕后调用JSON.parse(editor.setEditorState)
  • EditorState-> 一个编辑器状态。它在幕后调用 - editor.setEditorState()(undo-redo/History 插件使用这个) (() => void)->editor.update函数

您感兴趣的是(() => void)-> 编辑器更新。

您可以运行editor.update如下所示:

<LexicalPlainTextPlugin initialEditorState={() => {
  const paragraph = $createParagraphNode();
  const text = $createTextNode('foo');
  paragraph.append(text);
  $getRoot().append(paragraph);
  $getRoot().selectEnd();
}} />
Run Code Online (Sandbox Code Playgroud)

无需缓存(useCallback)initialEditorState,因为它只处理一次

旁注:我们计划搬到initialEditorState(目前居住在LexicalPlainTextPluginLexicalRichTextPlugin),LexicalComposer但它会以同样的方式工作。


我们建议也避免手动制作的解决方案:

// PrepopulatePlugin.js
useLayoutEffect(() => {
  editor.update(() => {
    // Prepopulate
  });
}, [editor]);
Run Code Online (Sandbox Code Playgroud)

我们构建了 LexicalContentEditable 来与 SSR 很好地配合并进行contenteditable适当的处​​理。如果您要构建自己的自定义解决方案,则必须重复此过程。


Jer*_*ier 10

您可以通过设置editorState为字符串化 JSON 状态或强制填充编辑器的函数来设置默认文本。

方法 #1 - 初始化editorState为字符串化 JSON 状态

editorState您可以通过设置为字符串化 JSON 状态来设置初始内容。

const EMPTY_CONTENT =
  '{"root":{"children":[{"children":[],"direction":null,"format":"","indent":0,"type":"paragraph","version":1}],"direction":null,"format":"","indent":0,"type":"root","version":1}}';

const initialConfig = {
  ...
  editorState: EMPTY_CONTENT,
  ...
}

const Editor = () => {

  return (
    <LexicalComposer initialConfig={initialConfig}>
      ...
    </LexicalComposer>
  );
}
  
Run Code Online (Sandbox Code Playgroud)

请注意,您可以通过以下方式检索 JSON 状态 onChangePlugin

import { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin";
...

  const onChange = (editorState) => {
    editorState.read(() => {
      const json = editorState.toJSON();
      console.log(JSON.stringify(json));
    })
  }
  ...
  
  return (
    <LexicalComposer initialConfig={initialConfig}>
      ...
      <OnChangePlugin onChange={onChange} />
    </LexicalComposer>

Run Code Online (Sandbox Code Playgroud)

方法 #2 - 初始化editorState为强制填充编辑器的函数

或者,您可以将其设置editorState为这样的函数,该函数强制初始化编辑器(这就是下面示例中的完成方式):

function prepopulatedRichText() {
  const root = $getRoot();
  if (root.getFirstChild() === null) {
    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 initialConfig = {
  ...
  editorState: prepopulatedRichText,
  ...
}
Run Code Online (Sandbox Code Playgroud)

以下是文档中的相关页面:

以下是默认使用初始文本的词法编辑器的示例:


小智 1

在词法代码库中找到的实现方法如下

\n
//...\n<RichTextPlugin\n   initialEditorState={ prepopulatedRichText}/>\n//...\n\nfunction prepopulatedRichText() {\n const root = $getRoot();\n if (root.getFirstChild() === null) {\n   const heading = $createHeadingNode(\'h1\');\n   heading.append($createTextNode(\'Welcome to the playground\'));\n   root.append(heading);\n   const quote = $createQuoteNode();\n   quote.append(\n     $createTextNode(\n       `In case you were wondering what the black box at the bottom is \xe2\x80\x93 it\'s the debug view, showing the current state of editor. ` +\n         `You can hide it by pressing on the settings control in the bottom-right of your screen and toggling the debug view setting.`,\n     ),\n   );\n   root.append(quote);\n   const paragraph = $createParagraphNode();\n   paragraph.append(\n     $createTextNode(\'The playground is a demo environment built with \'),\n     $createTextNode(\'@lexical/react\').toggleFormat(\'code\'),\n     $createTextNode(\'.\'),\n     $createTextNode(\' Try typing in \'),\n     $createTextNode(\'some text\').toggleFormat(\'bold\'),\n     $createTextNode(\' with \'),\n     $createTextNode(\'different\').toggleFormat(\'italic\'),\n     $createTextNode(\' formats.\'),\n   );\n   root.append(paragraph);\n   const paragraph2 = $createParagraphNode();\n   paragraph2.append(\n     $createTextNode(\n       \'Make sure to check out the various plugins in the toolbar. You can also use #hashtags or @-mentions too!\',\n     ),\n   );\n   root.append(paragraph2);\n   const paragraph3 = $createParagraphNode();\n   paragraph3.append(\n     $createTextNode(`If you\'d like to find out more about Lexical, you can:`),\n   );\n   root.append(paragraph3);\n   const list = $createListNode(\'bullet\');\n   list.append(\n     $createListItemNode().append(\n       $createTextNode(`Visit the `),\n       $createLinkNode(\'https://lexical.dev/\').append(\n         $createTextNode(\'Lexical website\'),\n       ),\n       $createTextNode(` for documentation and more information.`),\n     ),\n     $createListItemNode().append(\n       $createTextNode(`Check out the code on our `),\n       $createLinkNode(\'https://github.com/facebook/lexical\').append(\n         $createTextNode(\'GitHub repository\'),\n       ),\n       $createTextNode(`.`),\n     ),\n     $createListItemNode().append(\n       $createTextNode(`Playground code can be found `),\n       $createLinkNode(\n         \'https://github.com/facebook/lexical/tree/main/packages/lexical-playground\',\n       ).append($createTextNode(\'here\')),\n       $createTextNode(`.`),\n     ),\n     $createListItemNode().append(\n       $createTextNode(`Join our `),\n       $createLinkNode(\'https://discord.com/invite/KmG4wQnnD9\').append(\n         $createTextNode(\'Discord Server\'),\n       ),\n       $createTextNode(` and chat with the team.`),\n     ),\n   );\n   root.append(list);\n   const paragraph4 = $createParagraphNode();\n   paragraph4.append(\n     $createTextNode(\n       `Lastly, we\'re constantly adding cool new features to this playground. So make sure you check back here when you next get a chance :).`,\n     ),\n   );\n   root.append(paragraph4);\n }\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n

但我更喜欢使用字符串化的editorState,然后通过实例拥有的parseEditorState函数将其解析为editorState。

\n

这是我感到困惑的另一个问题,这似乎也让我错过了一些东西。

\n

ParseEditorState是一个仅用于实例的函数,但我不能在init组件下使用useLexicalComposerContext,它将返回LexicalComposer(会导致意外错误),所以我需要编写一个额外的插件来实现它。像这样的东西:

\n
import { useSelector } from "react-redux";\nimport { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";\nimport { useEffect } from "react";\n\nexport default function EditorStatePlugin() {\n  const activeNote = useSelector((state) => state.note.activeNote);\n  const [editor] = useLexicalComposerContext();\n  const state = editor.parseEditorState(\n    activeNote?.content ||\n      \'{"_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"}}\'\n  );\n  useEffect(() => {\n    editor.setEditorState(state);\n  }, [activeNote]);\n\n  return null;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

感觉写法不太好,请问有更好的写法吗?

\n