How can I reset a dragged component to its original position with react-draggable?

Viewed 3303

I try to implement a function in my app that allows the user to reset all the components that he dragged around to be reset to their original position.

I assume that this functionality exists in react-draggable because of this closed and released issue: "Allow reset of dragging position" (https://github.com/idanen/react-draggable/issues/7). However I did not find any hint in the documentation (https://www.npmjs.com/package/react-draggable).

There was one question with the same content in stackoverflow, but it has been removed (https://stackoverflow.com/questions/61593112/how-to-reset-to-default-position-react-draggable).

Thanks for your help :-)

5 Answers

The referenced issue on the GitHub references a commit. After taking a look at the changes made in this commit, I found a resetState callback added to the useDraggable hook. In another place in the commit, I found a change to the test file which shows usage of the hook.

function Consumer(props) {
    const {
      targetRef,
      handleRef,
      getTargetProps,
      resetState,
      delta,
      dragging
    } = useDraggable(props);
    const { style = defaultStyle } = props;
    return (
      <main
        className='container'
        ref={targetRef}
        data-testid='main'
        style={style}
        {...getTargetProps()}
      >
        {dragging && <span>Dragging to:</span>}
        <output>
          {delta.x}, {delta.y}
        </output>
        <button className='handle' ref={handleRef}>
          handle
        </button>
        <button onClick={resetState}>reset</button>
      </main>
    );
  }

The hook returns a set of callbacks, including this callback, which can be used to reset the state of the draggable.

You can do this in a very haphazard manner. There may be another way to set state more safely on this but I didn't look too deeply into it.

import React from 'react';

export default class  extends Component {
    constructor(props) {
        super(props);
        this.draggableEntity = React.createRef();
    }

    resetDraggable() {
        try {
            this.draggableEntity.current.state.x = 0;
            this.draggableEntity.current.state.y = 0;
        } catch (err) {
            // Fail silently
        }
    
    }
    
    render() {
        return (
            <Draggable 
            ref={this.draggableEntity}
            >
               <img onClick={(e) => {this.resetDraggable()}}></img>
            </Draggable>
        )
    }
}

I wanted the component to reset back to its original position when the component was dropped.

Using hooks I monitored if the component was being dragged and when it was false reset the position otherwise it would be undefined.

export default function DraggableComponent(props: any) {
    const {label} = props
    const [isDragging, setIsDragging] = useState<boolean>(false)

    const handleStart = (event: any, info: DraggableData) => {
         setIsDragging(true)
    }
    const handleStop = (event: any, info: DraggableData) => {
        setIsDragging(false)
    }
    return (
        <Draggable
          onStart={handleStart}
          onStop={handleStop}
          position={!isDragging? { x: 0, y: 0 } : undefined}
        >
          <Item>
             {label}
          </Item>
        </Draggable>
       )
 }

Simple approach would be:

  • creating a new component to wrap our functionality around the Draggable callbacks
  • reset position when onStop callback is triggered

Example:

import { useState } from 'react';
import Draggable, { DraggableData, DraggableEvent, DraggableProps } from 'react-draggable';

export function Drag({ children, onStop, ...rest }: Partial<DraggableProps>) {
  
  const initial = { x: 0, y: 0 }
  const [pos, setPos] = useState(initial)

  function _onStop(e: DraggableEvent, data: DraggableData){
      setPos(initial)
      onStop?.(e, data)
  }
  
  return (
      <Draggable position={pos} onStop={_onStop} {...rest}>
        {children}
      </Draggable>
  )
}

Usage:

export function App() {
  return (
      <Drag> Drag me </Drag>
  )
}

Note that this answer does not work.

None of these approaches worked for me, but tobi2424's post on issue 214 of the Draggable repo did. Here's a minimal proof-of-concept:

import React from "react";
import Draggable from "react-draggable";

const DragComponent = () => {
  // Updates the drag position parameter passed to Draggable
  const [dragPosition, setDragPosition] = React.useState(null);

  // Fires when the user stops dragging the element
  const choiceHandler = () => {
    setDragPosition({x: 0, y: 0});
  };

  return (
    <Draggable
      onStop={choiceHandler}
      position={dragPosition}
    >
      Drag me
    </Draggable>
  );
};

export default DragComponent;

Edit

The code above works intermittently but not particularly well. As far as I can work out, react-draggable stores data about the position of the dragged element somewhere outside of React, in order to preserve the position of the element between component refreshes. I was unable to determine how to reset the position of the element on command and none of the other example code solves the problem for me.

Related