ReactJS: Why isn't my draggable pop-up functional component staying under the mouse when dragged?

Viewed 30

Hi everyone & thanks in advance for your help!

I am working on a custom draggable pop-up functional component, but the dragging isn't working as intended and the div does not follow under the mouse.

I had some extra logic in there to handle the x & y offset of the mouse inside the div, but I removed that to first debug why the dragging isn't working properly.

import React, {useState} from 'react';
import './DraggablePopup.css';

const DraggablePopup = () => {
    const [isDragging, setIsDragging] = useState(false);
    const [styles, setStyles] = useState({});

    const handleDragStart = (e) => {                
        console.log('Drag Start');
        setIsDragging(true);        
    }

    const handleDragging = (e) => {        
        if( isDragging ) {
            let left = e.screenX;
            let top = e.screenY;
            setStyles({
                left: left,
                top: top
            });
        }
    }

    const handleDragEnd = () => {
        console.log('Drag End');
        setIsDragging(false);
    }

    return (
        <div
            className={'draggable-popup'}    // position: absolute
            style={styles}
            onMouseDown={handleDragStart}
            onMouseMove={handleDragging}
            onMouseUp={handleDragEnd}
        >
            <div className={'draggable-popup-header'}>
                Header
            </div>
            <div className={'draggable-popup-body'}>
                This is the draggable popup body
            </div>
        </div>
    );
}

export default DraggablePopup;
1 Answers

You need a ref to store the previous position

import React, { useState, useRef } from 'react';
import './DraggablePopup.css';

const DraggablePopup = () => {
  const [isDragging, setIsDragging] = useState(false);
  const [styles, setStyles] = useState({});
  const firstPos = useRef(null);
  const dragElementRef = useRef(null);

  const handleDragStart = (e) => {
    firstPos.current = {
      x: e.clientX,
      y: e.clientY,
      container: dragElementRef.current.getBoundingClientRect()
    };
    setIsDragging(true);
  };

  const handleDragging = (e) => {
    if (isDragging) {
      let left =
        firstPos.current.container.left + e.clientX - firstPos.current.x;
      let top = firstPos.current.container.top + e.clientY - firstPos.current.y;
      setStyles({
        left: left,
        top: top
      });
    }
  };

  const handleDragEnd = (e) => {
    console.log("Drag End");
    setIsDragging(false);
  };

  return (
    <div
      className={"draggable-popup"} // position: absolute
      style={styles}
      onMouseDown={handleDragStart}
      onMouseMove={handleDragging}
      onMouseUp={handleDragEnd}
      ref={dragElementRef}
    >
      <div className={"draggable-popup-header"}>Header</div>
      <div className={"draggable-popup-body"}>
        This is the draggable popup body
      </div>
    </div>
  );
}

You can check in my codesandbox. Hope it help!

Related