我們知道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);

具體使用細節可以參照我前幾天寫的 《Did you mean to use React.forwardRef()?搞懂react的createRef和forwardRef》

用法二:讓我們在父組件裏面能通過 ref 拿到函數式子組件需要通過 useImperativeHandle 暴露的方法

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() 獲取到富文本里面的內容了。

相關文章