原文转载自道招网的《改造富文本编辑器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 对象的 refcreateHandle
:一个函数,返回值是一个对象,即这个 ref 的current[deps]
:即依赖列表,当监听的依赖发生变化,useImperativeHandle 才会重新将子组件的实例属性输出到父组件ref 的 current 属性上,如果为空数组,则不会重新输出。
现在我们就可以通过this.richEditorRef.current.getContent()
获取到富文本里面的内容了。