react-quill使用服务端上传图片handlers导致中文输入问题-原理分析

背景

富文本编辑器:react-quill

当服务端的方法上传图片时,导致输入问题

打字时,导致打一个英文,失焦,而打中文字时,则导致只能打出拼音的第一个字母,中文出不来

解释:用服务端上传图片而不是默认的base64,需要用handlers

现象

1.如果有handlers,且onChange里面用了setState或者dispatch,会导致打字有问题

2.如果handlers去掉,打字没问题

3.handers绑定的image函数为空函数,打字还是有问题

4.有handlers,但是没有重新触发渲染(setState、dispatch),打字没问题

问题代码

Class写法

js 复制代码
<ReactQuill
        ref={ref => {this.state.myRef = ref}}
        style={{height:'120px'}}
        key='detail'
        theme="snow"
        modules={
          {
            toolbar: {
              container: this.state.container,
              handlers: {
                image: () => this.imageHandler()
              }
            },
            clipboard: {
              // toggle to add extra line breaks when pasting HTML
              matchVisual: false,
            }
          }
        }
        value={this.props.default || ''}
        onChange={this.handleChange}
      />

handleChange里面如果用setState或者dispatch(设置store) Hook写法

js 复制代码
function 业务组件(){
    const imageHandler = () => {
      //服务端上传图片
    }
    return (
        <ReactQuill
            ref={quillRef}
            value={tourBasicObj.detailIntroductionContent || ''}
            placeholder="请输入介绍内容"
            onChange={value => handleDetailIntroductionContentChange(value)}
            style={{ width: 385 }}
            modules={{
                toolbar: {
                  container,
                  handlers: {
                    image: imageHandler,
                  },
                },
              }
            }
          />

    )

}

初步猜测

是因为重新render导致quill库认为重新绑定hander,进而重新初始化,因此失焦

寻找根因

源代码结构

1.业务代码使用react-quill

2.react-quill库使用quill,作用是把quill适配成react可以用的库

3.quill才是富文本编辑器的库,富文本编辑功能都在此

第一步

排查为什么有了handlers,且onChange里面有了setState,即使handlers挂载空函数,也会导致失焦

根据ai提示与react-quill源码阅读,猜测可能是由于有了handlers,导致modules改变,进而触发某种重载

为什么onChange里面有了setState,就会有问题

这是由于setState会触发render,重新加载组件

react-quill源码截图

因此尝试调试排查,是因为重新渲染的时候,由于有了handlers,所以导致modules不同吗

本地调试代码模拟如下

父组件

子组件

打一个字,触发setState,导致重新渲染(模拟打字场景)

打印结果

由此结果证明

有了handlers,会导致modules发生变化

第二步

由于有了handlers改变,导致modules发生改变,那么我们需要在源码中寻找答案,由于modules改变,是触发了handers重新挂在或者整个编辑器重新加载

通过在源码里面加console的方法,调试得出结论

由于modules改变,导致触发了unhookEdtior和createEditor,因此导致失焦

调试结果

没有handlers

有handlers

对比得出关键代码,有handlers会触发unhookEditor和createEditor

源码中关键代码截图与解释

1.在生命周期钩子componentWillReceiveProps里面会调用shouldComponentRegenerate,会判断是否为true

2.由于有了handlers,会导致modules不同,因此为true

3.由于为true,因此调用regenerate方法

4.generation方法导致generation状态更新

5.在生命周期钩子componentWillUpdate会判断,generation是否一致,由于状态已经更新,因此不一致,所以调用componentWillUnmount

在componentWillUnmount会触发unhookEdit,导致富文本编辑器失去监听

于是有了答案,由于hanlder,导致modules更新,进而触发unhookEditor和createEditor也就是编辑器重载,因此失焦,编辑器渲染之后是不会主动聚焦的,所以也就解释了为什么输入一个中文,就变成拼音且失焦,输了一个英文字母,就失焦

第三步

探寻为什么有了handlers,会导致modules更新

通过再次复习react,发现答案

如果是函数式写法,发现只要setState,就会触发render,render的时候就会触发所有方法的重载,如果不想要方法重载,就必须用特殊方法,例如把方法放到函数组件外等

如果是Class写法,由于绑定的函数需要用到this或者this.setState,一般来说必须用bind(this)或者用箭头函数,但是如果用了箭头函数,render的时候,就会导致加载了新的方法

结论

根因:由于打字后,onChange里面有setState,触发重新render,导致imageHandler重新定义,最终传给react-quill组件的时候,导致modules发生了变化,最后触发了取消监听editor,再监听editor,进而导致不聚焦(取消监听并重新注册的这个间隙,浏览器会认为编辑区失去了活动状态(尤其是 IME 输入法正在 composition 中时)------这就是失焦发生的地方)

解决方案

Hook函数式写法

1. useMemo

由于useMemo对整个modules做了缓存,保证modules不会发生改变,因此不会触发editor的卸载与重载,进而保持打字正常

2.useRef

3.useCallback

单独对image做useCallback也可以

imageHandlerCb = useCallback(imageHandler,[]);

总而言之,是因为handlers导致modules改变,因此只要让handlers不改变或者modules不改变即可

Class写法

由于modules用的是this.modules,而this.modules只会在初始化执行一次,因此不会改变

相关推荐
OLong12 分钟前
React Update Queue 源码全链路解析:从 setState 到 DOM 更新
前端·react.js
知识浅谈15 分钟前
OpenLayers与Vue.js结合实现前端地图应用
前端
答案answer35 分钟前
three.js 实现几个好看的文本内容效果
前端·webgl·three.js
Running_C43 分钟前
一文读懂跨域
前端·http·面试
南囝coding1 小时前
这个Web新API让任何内容都能画中画!
前端·后端
起这个名字1 小时前
Vue2/3 v-model 使用区别详解,不了解的来看看
前端·javascript·vue.js
林太白1 小时前
VitePress项目工程化应该如何做
前端·后端
七夜zippoe1 小时前
Chrome 插件开发实战
前端·chrome·插件开发
ScottePerk1 小时前
css之再谈浮动定位float(深入理解篇)
前端·css·float·浮动布局·clear
RiemannHypo1 小时前
Vue3.x 全家桶 | 12 - Vue 的指令 : v-bind
前端