Skip to content

如何在react hook中使用Vditor #1080

Closed
@ghost

Description

描述问题

react hook中使用Vditor时,出现以下问题:
代码:

import React,{ useRef } from 'react'
import Vditor from 'vditor'

const Markdown = () => {
  let editor = useRef(null)
  let vditor = new Vditor(editor, {
    height: 360,
    toolbarConfig: {
      pin: true
    },
    cache: {
      enable: false
    },
    after() {
      vditor.setValue('Hello, Vditor + React!')
    }
  })
  return (
      <div ref={editor} />
  )
}

export default Markdown

控制台报错:

Uncaught (in promise) TypeError: Cannot add property innerHTML, object is not extensible
    at initUI (index.min.js:6245)
    at index.min.js:15342

期待的结果

能否出一个简单的react hook使用示例代码

版本信息

  • 版本:3.8.5
  • 操作系统:macOS 11.2
  • 浏览器:google 92.0

Activity

BeiyanYunyi

BeiyanYunyi commented on Sep 13, 2021

@BeiyanYunyi

我用 id 还行,ref 可能并不是一个 element。JSX.Element 和 DOM 的 Element 应该还是有差别的。

ghost

ghost commented on Sep 14, 2021

@ghost
BeiyanYunyi

BeiyanYunyi commented on Sep 14, 2021

@BeiyanYunyi
// App.tsx
import React from "react";
import Vditor from "vditor";
import "vditor/src/assets/scss/index.scss";

const App = () => {
  const [vd, setVd] = React.useState<Vditor>();
  React.useEffect(() => {
    if (!vd) {
      const vditor = new Vditor("vditor", {
        after: () => {
          setVd(vditor);
        },
      });
    }
  }, [vd]);
  React.useEffect(() => {
    if (vd) vd.setValue("`Vditor` 最小代码示例");
  }, [vd]);
  return <div id="vditor" className="vditor" />;
};

export default App;

依赖项上,需要安装 sass 来正确显示样式,当然也可以在代码里加载 jsdelivr 上的 css 。
这是我的代码,其中 vd 会被设置为 Vditor 的实例以进行其他操作,如果不在其它地方使用的话,也可以不 useState()
btw,react-vditorReadme 里也有用 hook 的示例代码。

ghost

ghost commented on Sep 14, 2021

@ghost
BeiyanYunyi

BeiyanYunyi commented on Sep 14, 2021

@BeiyanYunyi

你的代码里,直接在组件函数内把 vditor 实例化是错误的。react 里,若组件状态有更新,则整个组件会被刷新,这样你那个实例化的过程就会被执行多次,带来性能上的损失,以及可能存在的 bug,比如你的代码实际上在自己的 DOM (return 的内容)被渲染出来之前就开始将 vditor 实例化了,这时 vditor 找不到那个 DOM。
正确的方法是像我的代码那样使用 useEffect,这个 hook 在其第二个参数(任意数组)的内容发生变动时才会运行其第一个参数(回调函数)。如果数组为空(如我的代码),则回调会在组件每次加载完成后被调用一次。按 react-vditor 的方法,性能会比我的更好一些(只要 vditor 实例不发生变动,就不再调用那段函数,相当于只在组件第一次加载时运行回调)

BeiyanYunyi

BeiyanYunyi commented on Sep 14, 2021

@BeiyanYunyi

另外我也建议作者更新一下 React 的示例代码。现在 React 流行 hook 增强的函数式组件,原来的类组件已经不那么常见了。我学 React 用的教程里,类组件被放在比较靠后的位置,成为了某种“高阶技巧 / 拓展阅读”。
React 官方 是这么写的:We recommend trying Hooks in new code.

ghost

ghost commented on Sep 14, 2021

@ghost
BeiyanYunyi

BeiyanYunyi commented on Sep 14, 2021

@BeiyanYunyi

setValue 是 vditor 的内置方法。我代码里没有出现这个。我给改一下。

BeiyanYunyi

BeiyanYunyi commented on Sep 14, 2021

@BeiyanYunyi

改好了,应该改得比较清楚了。

ghost

ghost commented on Sep 14, 2021

@ghost
ghost

ghost commented on Sep 14, 2021

@ghost

改好了,应该改得比较清楚了。

感谢你的回答,辛苦辛苦

ghost closed this as completedon Sep 14, 2021
HengCC

HengCC commented on Feb 25, 2022

@HengCC

我也改好了。刚刚又试了一下,发现不在useEffect中是不能直接渲染的。我之前确实试过在不在useEffect中都是一样的,但现在的表现就是在useEffect中是可以的。😅难搞哦

// App.tsx
import React from "react";
import Vditor from "vditor";
import "vditor/src/assets/scss/index.scss";

const App = () => {
  const [vd, setVd] = React.useState<Vditor>();
  React.useEffect(() => {
    if (!vd) {
      const vditor = new Vditor("vditor", {
        after: () => {
          setVd(vditor);
        },
      });
    }
  }, [vd]);
  React.useEffect(() => {
    if (vd) vd.setValue("`Vditor` 最小代码示例");
  }, [vd]);
  return <div id="vditor" className="vditor" />;
};

export default App;

依赖项上,需要安装 sass 来正确显示样式,当然也可以在代码里加载 jsdelivr 上的 css 。 这是我的代码,其中 vd 会被设置为 Vditor 的实例以进行其他操作,如果不在其它地方使用的话,也可以不 useState()。 btw,react-vditorReadme 里也有用 hook 的示例代码。

遇到了个比较奇怪的问题. 初始化过程和你一样. 初始化的时候加了定制的toolbar. 如下

{
    name: 'cancel',
    tipPosition: 's',
    tip: '取消',
    icon: '',
    click: () => {
      cancelEdit();
    },
 },

以上click被执行的时候会调用

 const cancelEdit = () => {
    console.log('markdown:', markdown);
    console.log('get value:', editor, editor?.getValue());
    editor?.setValue(markdown, true);
    setPreviewOnly(true);
  };

但是这个位置打印出来的editor是空的.. editor声明如下:
const [editor, setEditor] = useState<Vditor>();

after后set值的

after: () => {
      setEditor(vd);
},

诡异的是下面这样的effect中. 是能够获取到editor的值, 但是上面的取消触发后就无法获取

  useEffect(() => {
    console.log('change mark edit:', markdown);
    console.log('change editor:', editor);
    editor?.setValue(markdown);
  }, [editor, markdown]);
1zumii

1zumii commented on Mar 6, 2022

@1zumii
// App.tsx
import React from "react";
import Vditor from "vditor";
import "vditor/src/assets/scss/index.scss";

... ...

依赖项上,需要安装 sass 来正确显示样式,当然也可以在代码里加载 jsdelivr 上的 css 。... ...

@lixiang810 我发现不用去 src/ 目录下引用 scss,我在 dist/ 目录下找到了打包好的 index.css。引入后也正常显示了。这样就省去了还需要使用 scss 的步骤。

.
├── LICENSE
├── README.md
├── dist
│   ├── css
│   ├── images
│   ├── index.css    # import 这个
│   ├── index.d.ts
│   ├── index.min.js
│   ├── js
│   ├── method.d.ts
│   ├── method.min.js
│   ├── ts
│   └── types
├── package.json
└── src
HengCC

HengCC commented on Mar 7, 2022

@HengCC

我也改好了。刚刚又试了一下,发现不在useEffect中是不能直接渲染的。我之前确实试过在不在useEffect中都是一样的,但现在的表现就是在useEffect中是可以的。😅难搞哦

// App.tsx
import React from "react";
import Vditor from "vditor";
import "vditor/src/assets/scss/index.scss";

const App = () => {
  const [vd, setVd] = React.useState<Vditor>();
  React.useEffect(() => {
    if (!vd) {
      const vditor = new Vditor("vditor", {
        after: () => {
          setVd(vditor);
        },
      });
    }
  }, [vd]);
  React.useEffect(() => {
    if (vd) vd.setValue("`Vditor` 最小代码示例");
  }, [vd]);
  return <div id="vditor" className="vditor" />;
};

export default App;

依赖项上,需要安装 sass 来正确显示样式,当然也可以在代码里加载 jsdelivr 上的 css 。 这是我的代码,其中 vd 会被设置为 Vditor 的实例以进行其他操作,如果不在其它地方使用的话,也可以不 useState()。 btw,react-vditorReadme 里也有用 hook 的示例代码。

遇到了个比较奇怪的问题. 初始化过程和你一样. 初始化的时候加了定制的toolbar. 如下

{
    name: 'cancel',
    tipPosition: 's',
    tip: '取消',
    icon: '',
    click: () => {
      cancelEdit();
    },
 },

以上click被执行的时候会调用

 const cancelEdit = () => {
    console.log('markdown:', markdown);
    console.log('get value:', editor, editor?.getValue());
    editor?.setValue(markdown, true);
    setPreviewOnly(true);
  };

但是这个位置打印出来的editor是空的.. editor声明如下: const [editor, setEditor] = useState<Vditor>();

after后set值的

after: () => {
      setEditor(vd);
},

诡异的是下面这样的effect中. 是能够获取到editor的值, 但是上面的取消触发后就无法获取

  useEffect(() => {
    console.log('change mark edit:', markdown);
    console.log('change editor:', editor);
    editor?.setValue(markdown);
  }, [editor, markdown]);

我说的这个问题我找到问题点了. 主要是effect机制以及在其中初始化的问题. 需要监听editor的变更. 不然第一次after后执行获取的editor永远为空.

BeiyanYunyi

BeiyanYunyi commented on Mar 7, 2022

@BeiyanYunyi

@HengCC 和 Effect 确实有关,但是我觉得核心在于,setState 这个函数本质是异步的,你 setState 完了并不能直接用。

HengCC

HengCC commented on Mar 8, 2022

@HengCC

本质上是effect的机制决定的. 副作用如果不指定依赖的话,那么在当时的执行环境中如果editor没被初始化. 那就是获取不到值. 即使是依赖了也得这么写才能有值.

useEffect(()=>{
    if(!editor){
       // 初始化 editor
    }else{
     //上面初始化后还会触发这个effect. 在这里可以做配置覆盖等. 或者调用方法. 本质上这个副作用只和editor相关.和上面的赋值markdown没啥关系. 这可能也是React官方推荐这样玩吧. 不同的依赖不同的副作用. 分离或者耦合组成不同的副作用.
    }

},[editor]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @HengCC@BeiyanYunyi@1zumii

        Issue actions

          如何在react hook中使用Vditor · Issue #1080 · Vanessa219/vditor