# 使用 UseEffect 的错误记录

背景:在 react 中对 monaco-editor 进行初始化,但是会发现有如下两个问题

  1. monaco-editor 创建的实例对象,被赋值了多次
  2. monaco-editor 对象被多次挂载销毁,最终要么是 dom 节点上压根就没有 monaco-editor, 要么是有 monaco-editor 相关信息,但是却没有编辑器

# 造成上述现象的 Bad Example

import { FC, useEffect, useRef, useState } from "react";
import styled from "styled-components";
import * as monaco from "monaco-editor";
const EditroContent = styled.div`
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  .editor-content-header {
  }
  .editor-content-body {
    width: 100%;
    height: 100%;
    background-color: lightblue;
  }
`;
const JSContent: FC = () => {
  const editorEl = useRef<HTMLDivElement>(null);
  const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>();
  const createEditor =async () => {
    if (!editorEl.current) return; // 确保 DOM 元素存在
    if (editorRef.current) {
      editorRef.current.dispose();
    }
    if (!editorRef.current) {
      if (editorEl.current) {
        editorRef.current = await monaco.editor.create(editorEl.current!, {
          model: null,
          minimap: {
            enabled: false, // 关闭小地图
          },
          wordWrap: "on", // 代码超出换行
          theme: "vs-dark", // 主题
          fontSize: 16,
          fontFamily: "MonoLisa, monospace",
          contextmenu: false, // 不显示右键菜单
          fixedOverflowWidgets: true, // 让语法提示层能溢出容器
          readOnly: false,
        });
      }
    }
  };
  useEffect(() => {
    console.log(editorEl.current);
    createEditor();
    
    console.log(editorRef.current);
    return () => {
      // 组件卸载时销毁编辑器实例
      if (editorRef.current) {
        editorRef.current.dispose();
      }
    };
  }, []);
  return (
    <>
      <EditroContent>
        <div className="editor-content-header">JS</div>
        <div className="editor-content-body" ref={editorEl}></div>
      </EditroContent>
    </>
  );
};
export default JSContent;

# 问题排查

# ques 1

  • step1: 本着要把单一职责的原理,我把创建 editor 实例的过程进行了抽离,但是造成了一系列错误。
  • step2: 已知 useEffect 会默认执行两次,所以当 console 报错 Element already has context attribute: editor-content-body 时,下意识的会猜测是否是因为 useEffect 默认执行了两次,加上 await 会有一个函数栈的堆积,阻塞进程,进而报错。
  • step3: 去除函数的 async await 毕竟 editor 实例挂载只需要在初始挂载时进行一次初始化即可

![img]

# ques2

step1:monaco-editor 对象被多次挂载销毁,考虑是否是添加了多次不必要的非空校验,通过 debugger 结果发现问题出现在 createEditor 阶段的判空问题

img

# 改进

import { FC, useEffect, useRef, useState } from "react";
import styled from "styled-components";
import * as monaco from "monaco-editor";
const EditroContent = styled.div`
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  .editor-content-header {
  }
  .editor-content-body {
    width: 100%;
    height: 100%;
    background-color: lightblue;
  }
`;
const JSContent: FC = () => {
  const editorEl = useRef<HTMLDivElement>(null);
  const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>();
  const createEditor = () => {
    if (!editorEl.current) return; // 确保 DOM 元素存在
    if (editorRef.current) {
      editorRef.current.dispose();
    }
    if (editorEl.current) {
      editorRef.current = monaco.editor.create(editorEl.current!, {
        language: "javascript", // Set the language to JavaScript
        minimap: { enabled: false },
        wordWrap: "on",
        theme: "vs-dark",
        fontSize: 16,
        fontFamily: "MonoLisa, monospace",
        contextmenu: false,
        fixedOverflowWidgets: true,
        readOnly: false,
      });
    }
  };
  useEffect(() => {
    createEditor();
    return () => {
      // 组件卸载时销毁编辑器实例
      if (editorRef.current) {
        editorRef.current.dispose();
      }
    };
  }, []);
  return (
    <>
      <EditroContent>
        <div className="editor-content-header">JS</div>
        <div className="editor-content-body" ref={editorEl}></div>
      </EditroContent>
    </>
  );
};
export default JSContent;
更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

神烦大眼怪 微信支付

微信支付

神烦大眼怪 支付宝

支付宝

神烦大眼怪 贝宝

贝宝