4

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 :-)

Arpit Vyas
  • 2,118
  • 1
  • 7
  • 18
N008Y
  • 43
  • 1
  • 4

7 Answers7

1

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.

Rodentman87
  • 640
  • 7
  • 14
1

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>
       )
 }
Mitchell Stone
  • 260
  • 2
  • 10
  • 1
    I tried this, it works great. But the only problem is that after reset when you again click on the draggable object it goes back to the original state where we left it before reset, Just using position={{x:0,y:0}} as a prop will do the work, no need of onStart or onStop – Santam Jun 06 '22 at 19:45
1

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>
  )
}
Kuza Grave
  • 1,256
  • 14
  • 15
1

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.

halfer
  • 19,824
  • 17
  • 99
  • 186
Ross Angus
  • 1,070
  • 8
  • 6
0

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>
        )
    }
}

lovgrandma
  • 133
  • 1
  • 9
0

There happens to be another way! You can use it's exposed ref element to reset its offset. This can be achieved like so:

import React, {useRef, useCallback} from "react";
import Draggable from "react-draggable";

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

  // Fires when the user stops dragging the element
  const resetDrag = useCallback(() => {
    setDragPosition({x: 0, y: 0});
    draggerRef.current?.setState({ x: 0, y: 0 }); // This is what resets it!
  }, [setDragPosition, draggerRef]);

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

export default DragComponent;
AP.
  • 8,082
  • 2
  • 24
  • 33
0

I find resolve for this problem, and this simple.

<Draggable
   axis="both"
   handle=".handle"
   defaultPosition={{x: 0, y: 0}}
   position={{x: 0, y: 0}}
   scale={1}
}}>

You just need set up position prop, so after onStop he will back to {x: 0, y: 0}.