14

改造wangEditor成react组件

 3 years ago
source link: https://www.daozhao.com/9821.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

我们知道wangEditor常用的功能是editor实例的 txt.html()txt.text() 方法,尤其是 txt.html() 方法,这是一个类似与jQuery常用的那种get和set一体的方法。

我们怎么把这种传统模式书写的第三方库引入到react项目中,并且方便其它同事使用呢?我们需要做一个react组件,让它来完成wangEditor的“react化”。

对于编辑器这种,我们不太在乎它的生命周期,我们更适合将它封装成函数式组件。

我们在项目使用是只有使用 ref 拿到这个组件并且调用对应的方法来取到(设置)富文本里面的内容。

<WangEditor 
    ref={this.richEditorRef} 
    content={this.state.editShortcutReply.content}/>

这样在使用时就很方便啊,那我们的WangEditor组件怎么实现呢?

import React, { useEffect, forwardRef, useImperativeHandle } from 'react';
import toaster from 'viewsUI/toaster';

import WangEditor from 'viewsUI/wangeditor/wangEditorForSetting'
import wangEditorI18nLang from './i18nLang';

let richEditor = null;

/*eslint-disable*/
const wangEditor = (props, ref) => {

  let unique = Math.random().toString(36).substr(2);

  useEffect(() => {
    console.log('wangEditor useEffect -> ');
    loadEditor();
    if (props.visible) {
      setContent(props.content || '');
    }
  }, []);

  function getContent() {
    return richEditor.txt.html();
  }

  function getText() {
    return richEditor.txt.text();
  }

  function setContent (content) {
    richEditor.txt.html(content);
    initFocus();
  }

  function initFocus() {
    const $textElem = richEditor.$textElem;
    const $children = $textElem.children();

    if ($children.length) {
      const $first = $children.first();
      richEditor.selection.createRangeByElem($first, false, true);
      richEditor.selection.restoreSelection();
    }
  }

  useImperativeHandle(ref, () => ({
    getContent,
    getText,
    setContent,
    initFocus,
  }));

  function loadEditor() {
    richEditor = new WangEditor('#toolbar' + unique, '#body' + unique);
    richEditor.customConfig.uploadImgShowBase64 = false;
    richEditor.customConfig.uploadImgMaxLength = 1
    richEditor.customConfig.uploadImgParams = {
      fileBytes: '',
      maxBytes: 204800,
      thumbHeight: 120,
      thumbWidth: 120,
    };
    richEditor.customConfig.menus = [
      'head', // 标题
      'bold', // 粗体
      'fontSize', // 字号
      'fontName', // 字体
      'italic', // 斜体
      'underline', // 下划线
      // 'strikeThrough', // 删除线
      'foreColor', // 文字颜色
      'backColor', // 背景颜色
      'link', // 插入链接
      'list', // 列表
      'justify', // 对齐方式
      'quote', // 引用
      //'emoticon', // 表情
      'image', // 插入图片
      // 'table', // 表格
      'htmlTable', // 粘贴表格
      // 'video', // 插入视频
      // 'code', // 插入代码
      'undo', // 撤销
      'redo' // 重复
    ];
    richEditor.create();
  }

  return (
    <div  style={{display: 'flex','flexDirection': 'column',width: '100%', height: '100%', overflow: 'auto'}}>
      <div id={'toolbar' + unique} style={{borderBottom: '1px solid #EBF2FA', fontSize: '14px', padding: '5px 10px', marginBottom: '5px', flex: 'none' }}/>
      <div id={'body' + unique} style={{height: 'auto',wordBreak: 'break-all',whiteSpace: 'normal',flex: 'auto',display: 'flex',flexDirection: 'column', overflow: 'auto'}}/>
    </div>
  );
};

export default forwardRef(wangEditor);

为了给父组件暴露wangEditor组件的部分方法,我们需要使用 useImperativeHandle

它可以让你在使用 ref 时自定义暴露给父组件的实例值。在大多数情况下,应当避免使用 ref 这样的命令式代码。 通常 useImperativeHandle 应当与 forwardRef 一起在函数式中使用。

forwardRef介绍

用法一:让我们在父组件里面能通过 ref 拿到函数式子组件的某个node节点

const Menu = (props, ref) => {
  return (<aside ref={ref}  id="menu" className={props.show ? 'show' : ''}>
    <div className="inner flex-row-vertical">
      <Profile avatarUrl="/owner.jpg"/>
      <div className="scroll-wrap flex-col">
        <MenuList asides={props.asides}/>
        <ArchiveList />
      </div>
    </div>
  </aside>);
};
export default forwardRef(Menu);

BZni22n.png!web

具体使用细节可以参照我前几天写的 《Did you mean to use React.forwardRef()?搞懂react的createRef和forwardRef》

用法二:让我们在父组件里面能通过 ref 拿到函数式子组件需要通过 useImperativeHandle 暴露的方法

3MfuIj.png!web

const Menu = (props, ref) => {
  function log(...rest) {
    console.log(...rest);
  }

  useImperativeHandle(ref, () => ({
    log
  }));

  return (<aside ref={ref}  id="menu" className={props.show ? 'show' : ''}>
    <div className="inner flex-row-vertical">
      <Profile avatarUrl="/owner.jpg"/>
      <div className="scroll-wrap flex-col">
        <MenuList asides={props.asides}/>
        <ArchiveList />
      </div>
    </div>
  </aside>);
};
export default forwardRef(Menu);

useImperativeHandle介绍

useImperativeHandle(ref, createHandle, [deps])

ref :定义 current 对象的 ref createHandle :一个函数,返回值是一个对象,即这个 ref 的current [deps] :即依赖列表,当监听的依赖发生变化,useImperativeHandle 才会重新将子组件的实例属性输出到父组件ref 的 current 属性上,如果为空数组,则不会重新输出。

现在我们就可以通过 this.richEditorRef.current.getContent() 获取到富文本里面的内容了。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK