cycleccc / wangEditor-next

wangEditor-next —— 基于 slate.js 的 Web 富文本编辑器。wangEditor-next —— web rich text editor, based on slate.js.
https://cycleccc.github.io/docs/
MIT License
75 stars 14 forks source link

Bug:`editor-for-react` 初始化时会执行 `onChange` #78

Open hsuna opened 1 month ago

hsuna commented 1 month ago

bug 描述

  1. 初始化页面数据时,并没有做任何输入操作,但是onChange事件会被触发;
  2. 光标位置不对,应该在末位,却在0位;

你预期的样子是?

  1. 用户输入后,才触发onChange事件。
  2. 光标位置在末位

系统和浏览器及版本号

wangEditor-next 版本

demo 能否复现该 bug ?

import "@wangeditor-next/editor/dist/css/style.css"; // 引入 css

import { useState, useEffect } from "react";
import { Editor, Toolbar } from "@wangeditor-next/editor-for-react";

const MyEditor = (props) => {
  const { value, onChange } = props || {};
  // editor 实例
  const [editor, setEditor] = useState(null); // JS 语法

  // 工具栏配置
  const toolbarConfig = {}; // JS 语法

  // 编辑器配置
  const editorConfig = {
    // JS 语法
    placeholder: "请输入内容...",
  };

  const hanleChange = (editor) => {
    console.log("hanleChange =>", editor.getHtml());
    onChange?.(editor.getHtml());
  };

  // 及时销毁 editor ,重要!
  useEffect(() => {
    return () => {
      if (editor == null) return;
      editor.destroy();
      setEditor(null);
    };
  }, [editor]);

  return (
    <div>
      <Toolbar editor={editor} defaultConfig={toolbarConfig} mode="default" />
      <Editor
        defaultConfig={editorConfig}
        value={value}
        onCreated={setEditor}
        onChange={hanleChange}
        mode="default"
      />
    </div>
  );
};

const App = () => {
  const [value, setValue] = useState(`<p>123</p>`);
  return <MyEditor value={value} onChange={(val) => setValue(val)} />;
};

export default App;

最小成本的复现步骤

期望是hanleChange不被触发,应该类似控件,用户输入发生变化后再触发的

cycleccc commented 1 month ago

https://github.com/cycleccc/wangEditor-for-react/blob/e5aa8714ce993d47b7cc0f41ee5080836de9d07e/src/components/Editor.tsx#L40

  1. 这一行逻辑实现了你第一条说的 onChange 默认触发,可以在项目里判断初始 editor.getHtml 和 useState默认值来跳过第一次change
  2. 光标也是,可以在初始化时使用editor.select api 来操作editor selection 选区,做到如 editor.deselect 取消选区 editor.select([]) 全选,以及editor.select(新的selection对象)做到部分选择和改变光标位置。
hsuna commented 1 month ago

我期望wangEditor的表现类似普通控件,如果是手动setHtml的情况,不应该onChange才对,如下:

const App = () => {
  const [value, setValue] = useState("");

  useEffect(() => {
    setTimeout(() => {
      setValue("test");
    }, 1000);
  }, []);

  const hanleChange = (evt) => {
    console.log("hanleChange => ", evt);
  };

  return <textarea value={value} onChange={hanleChange} />;
};

即便我延迟setValue了,焦点也在输入框中,hanleChange事件依然不会触发,这才是控件本应该的表现,否则这里处理时,心智太高了

hsuna commented 1 month ago

这样的问题我觉得,是因为非标的value在进行规格化后,与原先的value不等才导致触发onChange的;

就像当我设置value默认为空字符串时,在未做任何操作的情况下,最终获取到的value却是<p><br></p>而导致事件触发;

但我开发的本能直觉却会觉得,即使我setHtml是非标的数据,只要我未对其进行改动,那获取回来的值,依然还是原有的才对,类似纯展示的场景。

之所以有此疑惑,还是因为我这边的需求入库的数据是md格式,所以才会有需要对html格式进行额外的transform,而额外的onChange,会让处理变得过于怪异且存在心智负担。

hsuna commented 1 month ago

类似文档中的demo,当我打开页面时,config.onChange事件直接触发了,这太过反直觉了。

cycleccc commented 1 month ago

嗯,确实这个应该是在 onCreated 时触发的,我看看怎么改下吧,你有思路吗,这个得在 editor 包改,for-react 不需要变动。

hsuna commented 1 month ago

稍微看了下堆栈,似乎是Slate 触发的事件,我尝试写了个demo,发现 Slate 一旦获取焦点,就触发onChane