改造wangEditor成react組件
我們知道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()
獲取到富文本里面的內容了。