组件的多个实例导致事件处理问题

问题描述 投票:0回答:1

我正在从头开始为我的 React 项目创建一个文本编辑器组件。它单独工作正常,但是当我在同一页面上添加另一个实例时,某些或有时所有功能键都会停止工作。另外,有时当我单击 insetImage 或 createLink 图标插入链接或图像时,提示出现的次数与该组件实例的数量一样多。我不知道发生了什么事。我尝试从各种来源获取解决方案,我什至尝试过 gpt 但问题仍然存在。我认为当我单击任何功能键时,所有实例的事件处理程序都会被一一调用(根据我的想法)。这是组件的代码:

import React, { useState, useEffect } from "react";
import ulIcon from "../../assets/svgs/list-ul-alt_svgrepo.com.svg";
import olIcon from "../../assets/svgs/list-ol_svgrepo.com.svg";
import attachMent from "../../assets/svgs/attachment-right-svgrepo-com.svg";
import negativeIcon from "../../assets/svgs/negative sign.svg";
import subscriptIcon from "../../assets/svgs/subscript-svgrepo-com.svg";
import superscriptIcon from "../../assets/svgs/superscript-svgrepo-com.svg";

export default function TextEditor() {
  const [editorText, setEditorText] = useState("Enter question here...");

  function handleChange(e) {
    setEditorText(e.target.value);
  }
  useEffect(() => {
    const handleFunctionalKeyClick = (event) => {
      console.log("triggered");
      const func = event.target.getAttribute("data-func");
      if (func) {
        if (func == "insertImage") {
          const url = prompt("Enter url here", "http://");
          if (url !== null) {
            document.execCommand(func, false, url);
          }
        } else {
          document.execCommand(func, false);
        }
      }
    };
    const functionalKeysContainer = document.querySelectorAll("[data-func]");
    const nodeList = Array.from(functionalKeysContainer);
    nodeList.forEach((node) => {
      node.addEventListener("click", handleFunctionalKeyClick);
    });

    return () => {
      nodeList.forEach((node) => {
        node.removeEventListener("click", handleFunctionalKeyClick);
      });
    };
  }, []);

  return (
    <div className="flex justify-center items-center flex-col w-full h-dvh">
      <div className="EditorContainer flex-col flex gap-2 relative w-fit">
        <div className="border-2 border-black rounded-xl p-2">
          <div
            className="w-[45rem] max-w-full h-fit max-h-20 overflow-y-scroll text-3xl outline-none"
            id="editor"
            contentEditable
            onChange={handleChange}
            dangerouslySetInnerHTML={{ __html: editorText }}
          />
        </div>
        <div
          id="functionalKeys"
          className="functions bg-white flex gap-1 items-center border-2 border-black rounded-md w-fit p-1  right-2 bottom-[0.5rem] m-auto select-none"
        >
          <div className="bold" title="Bold" data-func="bold">
            B
          </div>
          <div className="pipe">|</div>
          <div className="italic" title="Italic" data-func="italic">
            I
          </div>
          <div className="pipe">|</div>
          <div className="underline" title="Underline" data-func="underline">
            U
          </div>
          <div className="pipe">|</div>
          <div
            className="unorderedList otherFunctionalIcons"
            title="unordered-list-icon"
          >
            <img
              src={ulIcon}
              alt="attachment icon"
              className="p-2 w-full cursor-pointer"
              title="unordered-list-icon"
              data-func="insertUnorderedList"
            />
          </div>
          <div className="pipe">|</div>
          <div
            className="attachment otherFunctionalIcons"
            title="ordered-list-icon"
          >
            <img
              src={olIcon}
              alt="attachment icon"
              className="p-2 w-full cursor-pointer"
              title="ordered-list-icon"
              data-func="insertOrderedList"
            />
          </div>
          <div className="pipe">|</div>
          <div className="attachment otherFunctionalIcons" title="subscript">
            <img
              src={subscriptIcon}
              alt="attachment icon"
              className="p-2 w-full cursor-pointer"
              title="subscript"
              data-func="subscript"
            />
          </div>
          <div className="pipe">|</div>
          <div className="attachment otherFunctionalIcons" title="superscript">
            <img
              src={superscriptIcon}
              alt="superscript icon"
              className="p-2 w-full cursor-pointer"
              title="superscript"
              data-func="superscript"
            />
          </div>
          <div className="pipe">|</div>
          <div className="attachment otherFunctionalIcons" title="Add image">
            <img
              src={attachMent}
              alt="attachment icon"
              className="p-2 w-full cursor-pointer"
              title="Add image"
              data-func="insertImage"
            />
          </div>
          <div className="pipe">|</div>
          <div
            className="removeDecoration p-2 active:bg-[var(--black25)] rounded-lg cursor-pointer"
            title="Remove all Decorations"
          >
            <img
              src={negativeIcon}
              alt=""
              className="h-6"
              title="Remove all Decorations"
              data-func="removeFormat"
            />
          </div>
        </div>
      </div>
    </div>
  );
}

目前,我不知道下一步该做什么?我询问了 GPT 但没有一个解决方案有效。

reactjs react-hooks event-handling state-management
1个回答
0
投票

您可以使用以下行将事件侦听器绑定到具有

[data-func]
属性的所有 DOM 元素:

const functionalKeysContainer = document.querySelectorAll("[data-func]");

因此,每个

TextEditor
组件都会将事件侦听器附加到先前安装的
TextEditor
组件的所有 DOM 元素。当您单击具有多个处理程序的元素时,所有处理程序都会立即触发。

解决方案 1 - 使用 React 事件系统而不是 DOM

手动将事件侦听器附加到每个按钮(或在循环中渲染它们)。删除

useEffect
块,提取
handleFunctionalKeyClick
函数,并将其更改为接受
func
字符串,而不是事件对象:

const handleFunctionalKeyClick = func => {
  if (func == "insertImage") {
    const url = prompt("Enter url here", "http://");
    if (url !== null) {
      document.execCommand(func, false, url);
    }
  } else {
    document.execCommand(func, false);
  }
};

注意:您也可以将其拆分为多个函数来解决特定的应用程序,例如处理“insertImage”。

现在向每个元素添加

onClick
属性,并传递
func
字符串,而不是使用
data-func
属性:

<img
  onClick={() => handleFunctionalKeyClick('insertUnorderedList')}
  src={ulIcon}
  alt="attachment icon"
  className="p-2 w-full cursor-pointer"
  title="unordered-list-icon"
/>

解决方案 2 - 为每个编辑器添加唯一的 DOM
id

如果必须通过 DOM 附加事件处理程序,则可以使用

id
钩子为每个组件生成唯一的
useId

export default function TextEditor() { const id = useId(); const [editorText, setEditorText] = useState("Enter question here...");
使用 

id

 代替静态“functionKeys”id:

<div id={id} className="functions bg-white flex gap-1 items-center border-2 border-black rounded-md w-fit p-1 right-2 bottom-[0.5rem] m-auto select-none" >

useEffect

 中附加事件处理程序时,使用 
id
 选择仅存在于该特定组件中的具有“[data-func]”属性的元素:

const functionalKeysContainer = document.querySelectorAll(`#${id} [data-func]`);
    
© www.soinside.com 2019 - 2024. All rights reserved.