Как грамотно организовать код drag&drop в компоненте React, как использовать addEventListener и removeEventListener

Проблема в следующем: Имеется 30 компонентов AlphabetCard, с атрибутом draggaable = true. Внутри компонента две функции handlerDragStart и handlerDragEnd запускающиеся через соответственно onDrag и onDragEnd компонентов AlphabetCard. Функция handlerDragStart обращается к сторонней функции вне компонента add_and_remove_eventListener, которая через ссылку в пробрасываемых пропсах на 30 div background_matrix_circle добавляет методом addEventListener прослушку событий dragEnter, dragOver, dragLeave и drop, одноименные функции которых лежат вне компонента. handlerDragEnd соответственно снимает их методом removeEventListener. Всё хорошо работает, но... стоит перенести функции handlerDragEnter, handlerDragOver, handlerDragLeave, handlerDrop внутрь компонента AlphabetCard, метод removeEventListener перестаёт работать и у div-ов background_matrix_circle в браузере во вкладке события, начинают множиться прослушиватели событий, на каждом dragEnter и т.п. список div растет.

Тоже самое происходит при попытке пробросить атрибуты из компонента AlphabetCard в функцию handlerDragDrop. При присвоении domElem.addEventListener("drop", handlerDragDrop, false); // нужно добавить в handlerDragDrop аттрибуты, но что я только не перепробывал, и через bind и внешнюю доп функцию, removeEventListener этот обработчик после удалить не может.

Читал, что связка компонентов и элементов dom дерева браузера очень плохая практика, и видимо я этот узел пишу не в логике React, а по привычке на чистом js. По этому вопрос к бывалым разработчикам, как решали подобные задачи? Какова логика drag&drop в React?

const handlerDragEnter = (event)=> {
  eventDefaultAndStopPropagation(event);
  event.target.classList.add("drop_insert_border_on");;
}
const handlerDragOver = (event)=> {
  eventDefaultAndStopPropagation(event);
}
const handlerDragLeave = (event)=> {
  eventDefaultAndStopPropagation(event);
  event.target.classList.remove("drop_insert_border_on");
}
const handlerDragDrop = (event) =>{
// !!! здесь нужно получить аттрибуты {backgroundLayerState,inputDataMatrixState} из AlphabetCard 
  eventDefaultAndStopPropagation(event);  
  event.target.classList.remove("drop_insert_border_on");
}

const add_and_remove_eventListener = (add,backgroundLayerState,inputDataMatrixState)=> {
  const divElements = backgroundLayerState.children;
    if (add) {
      // Добавить обработчики событий
      for(let domElem of divElements){
        domElem.addEventListener("dragenter", handlerDragEnter, false);
        domElem.addEventListener("dragover", handlerDragOver, false);
        domElem.addEventListener("dragleave", handlerDragLeave, false);
        domElem.addEventListener("drop",handlerDragDrop, false); // !!! добавить аттрибуты
      }
    } else {
      // Удалить обработчики событий
      for(let domElem of divElements){
        domElem.removeEventListener("dragenter", handlerDragEnter, false);
        domElem.removeEventListener("dragover", handlerDragOver, false);
        domElem.removeEventListener("dragleave", handlerDragLeave, false);
        domElem.removeEventListener("drop", handlerDragDrop, false);
      }
    }
}
function AlphabetCard({ props, draggableElemRef, inputDataMatrixState, backgroundLayerState }) {
  console.log("AlphabetCard рендер");
  const [transfer, setTransfer] = useState(false);
  const thisRef = useRef(null);

  const handlerDragStart = (event)=>{
    if(transfer) return;
    draggableElemRef.current = thisRef.current;
    eventDefaultAndStopPropagation(event);
    setTransfer(true);
    add_and_remove_eventListener(true,backgroundLayerState,inputDataMatrixState);
    console.log("dragStart");
  };

  const handlerDragEnd=(event)=> {
    console.log("dragEnd");
    eventDefaultAndStopPropagation(event);
    add_and_remove_eventListener(false,backgroundLayerState,inputDataMatrixState);
    setTransfer(false);
}

  const circleInCard = props.value.split(" ").map((elem, index) => {
    return <AlphabetCardCircle key={`alphabet_card_circle_${elem}_${index}`} props={elem} />;
  });

  return (
    <div
    ref={thisRef}
      className={`alphabet_card grid_el_${props.index} ${props.addClass} ${transfer ? "drag_start_end" : ""}`}
      data-value={props.value}
      draggable="true"
      onDragEnd={handlerDragEnd}
      onDrag={handlerDragStart}
    >
      {circleInCard}
    </div>
  );
}

Ответы (0 шт):