Search code examples
javascriptreactjspopupdraggabledrag

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


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;

Solution

  • 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!