reactjstypescriptlexicaljs

Lexicaljs receive editor state json and text content using debounce in react project


Requirement

I have a requirement to get the editor state in JSON format as well as the text content of the editor. In addition, I want to receive these values in the debounced way.

I wanted to get these values (as debounced) because I wanted to send them to my server.

Dependencies

"react": "^18.2.0",

"lexical": "^0.3.8",

"@lexical/react": "^0.3.8",


Solution

  • You don't need to touch any of Lexical's internals for this; a custom hook that reads and "stashes" the editor state into a ref and sets up a debounced callback (via use-debounce here, but you can use whatever implementation you like) is enough.

    function useDebouncedLexicalOnChange<T>(
      getEditorState: (editorState: EditorState) => T,
      callback: (value: T) => void,
      delay: number
    ) {
      const lastPayloadRef = React.useRef<T | null>(null);
      const callbackRef = React.useRef<(arg: T) => void | null>(callback);
      React.useEffect(() => {
        callbackRef.current = callback;
      }, [callback]);
      const callCallbackWithLastPayload = React.useCallback(() => {
        if (lastPayloadRef.current) {
          callbackRef.current?.(lastPayloadRef.current);
        }
      }, []);
      const call = useDebouncedCallback(callCallbackWithLastPayload, delay);
      const onChange = React.useCallback(
        (editorState) => {
          editorState.read(() => {
            lastPayloadRef.current = getEditorState(editorState);
            call();
          });
        },
        [call, getEditorState]
      );
      return onChange;
    }
    
    // ...
    
    const getEditorState = (editorState: EditorState) => ({
      text: $getRoot().getTextContent(false),
      stateJson: JSON.stringify(editorState)
    });
    
    function App() {
      const debouncedOnChange = React.useCallback((value) => {
        console.log(new Date(), value);
        // TODO: send to server
      }, []);
      const onChange = useDebouncedLexicalOnChange(
        getEditorState,
        debouncedOnChange,
        1000
      );
      // ...
      <OnChangePlugin onChange={onChange} /> 
    }