nhn / tui.editor

🍞📝 Markdown WYSIWYG Editor. GFM Standard + Chart & UML Extensible.
http://ui.toast.com/tui-editor
MIT License
17.08k stars 1.73k forks source link

Cannot do two way binding with React #1556

Closed ustacryptact closed 3 years ago

ustacryptact commented 3 years ago

I want to do two way binding with React.

However, every time I call setMarkdown method of the instance, the cursor is moved to the end of the editor.

Is there a way to workaround this?

gif

Example code:

import "@toast-ui/editor/dist/toastui-editor.css";
import "codemirror/lib/codemirror.css";
import { Editor } from "@toast-ui/react-editor";
import * as React from "react";

export default function App() {
  const [markdown, setMarkdown] = React.useState("");

  const editorRef = React.useRef<Editor>(null);

  React.useEffect(() => {
    editorRef.current?.getInstance().setMarkdown(markdown);
  }, [markdown]);

  return (
    <Editor
      ref={editorRef}
      onChange={() =>
        setMarkdown(editorRef.current?.getInstance().getMarkdown() ?? "")
      }
    />
  );
}

Codesandbox: https://codesandbox.io/s/xenodochial-ramanujan-yqm62?file=/src/App.tsx

js87zz commented 3 years ago

@ustacryptact It doesn't look like a bug. The focus is reset because re-rendering is triggered by setMarkdown hook. I think it would be appropriate to use a ref, not a setState hook. But what's the point of doing this?

ustacryptact commented 3 years ago

The point is, if the content is changed from somewhere else outside of the editor, it can be easily reflected in the editor.

For example: Using the editor inside a form and resetting it after a submission.

By the way, a workaround for this is by setting a flag like shouldForceUpdate to be sent along with the markdown content. So if the content is set from outside, shouldForceUpdate is true and if the content is set from the editor, shouldForceUpdate is false.

To trigger updates from outside, an effect like the following can be used within the component:

   React.useEffect(() => {
     if (props.markdown.shouldForceUpdate) {
         editorRef.current?.getInstance().setMarkdown(props.markdown.text);
         props.setMarkdown({ ...props.markdown, shouldForceUpdate: false });
     }
   }, [props.markdown]);
js87zz commented 3 years ago

@ustacryptact We didn't consider like this functionality. It is usually correct that the content is rendered again after submitting, unless features such as Collaborative editing are supported.

ustacryptact commented 3 years ago

For the version 3. I think it would be good to have the editor as a Controlled Component in React version.

So that the developers using React can have better control on the editor content.

mikhail-fedosenko commented 1 year ago

Is this now implemented in Editor or still abandoned? Maybe someone could suggest the approach to use the editor with React form libraries like react-hook-form? They require onChange callback and value prop on the controlled component. I can tie the onChange callback, but if i set value on every change - this causes the cursos to jump to the end of text...