reactでドラッグアンドドロップを実装する

動機

reactを使ってドラッグアンドドロップを実装したいときに, npmのライブラリで使えそうなのは以下のものがある.

  1. https://github.com/react-dnd/react-dnd
  2. https://github.com/react-grid-layout/react-draggable
  3. https://github.com/atlassian/react-beautiful-dnd

ただし,

  • ドラッグアンドドロップを実装するのにそんなライブラリがいるのか疑問がある.
  • こまかくカスタマイズできるドラッグアンドロップ機能が欲しかった.

という理由があったので自前で実装してみた. 本体は50行程度で実装できた.

type DragBoxProp = {
    width: number,
    height: number,
    initX: number,
    initY: number
}

const DragBox = ({ width, height, initX, initY }: DragBoxProp) => {
    const [x, setX] = useState(initX);
    const [y, setY] = useState(initY);
    const isDrg = useRef(false);
    const ox = useRef(0);
    const oy = useRef(0);
    useEffect(() => {
        setX(initX);
        setY(initY);
    }, [initX, initY]);
    const onMove = useCallback((e: any) => {
        if (!isDrg.current) return;
        let bd = e.target.ownerDocument.scrollingElement;
        const px = e.clientX + bd.scrollLeft - ox.current;
        const py = e.clientY + bd.scrollTop - oy.current;
        setX(px);
        setY(py);
    }, [setX, setY]);
    const _onUp = useCallback(async (e: any) => {
        ox.current = 0;
        oy.current = 0;
        isDrg.current = false;
        let el = e.target.ownerDocument;
        el.removeEventListener('mousemove', onMove, { capture: true });
        el.removeEventListener('mouseup', _onUp, { capture: true });
    }, [onMove, x, y]);
    const _onDown = useCallback(async (e: any) => {
        ox.current = e.nativeEvent.offsetX;
        oy.current = e.nativeEvent.offsetY;
        isDrg.current = true;
        let el = e.target.ownerDocument;
        el.addEventListener('mouseup', _onUp, { capture: true });
        el.addEventListener('mousemove', onMove, { capture: true });
    }, [_onUp, onMove, x, y]);
    return (
        <div onMouseUp={_onUp} onMouseDown={_onDown} style={{ width: width, height: height, background: "red", position: 'absolute', left: x, top: y, }} />
    );
}

動くプロジェクトはこちら. https://github.com/mk-system/react-drag 結果

はまったポイント

普通に実装すると, カーソルがコンポーネントからはみでたタイミングで反応しなくなってしまう.

そのため親のコンポーネントに動的にcallbackをフックする必要があった.

この記事をシェア

弊社では、一緒に会社を面白くしてくれる仲間を募集しています。
お気軽にお問い合わせください!