asliwinski
asliwinski

Reputation: 1714

How to attach onDragStart to Draggable in react-beautiful-dnd?

In the docs it says:

DragHandleProps need to be applied to the node that you want to be the drag handle. This is a number of props that need to be applied to the <Draggable /> node. The simplest approach is to spread the props onto the draggable node ({...provided.dragHandleProps}). However, you are also welcome to monkey patch these props if you also need to respond to them. DragHandleProps will be null when isDragDisabled is set to true.

dragHandleProps Type information

type DragHandleProps = {|
  // what draggable the handle belongs to
  'data-rbd-drag-handle-draggable-id': DraggableId,

  // What DragDropContext the drag handle is in
  'data-rbd-drag-handle-context-id': ContextId,

  // Id of hidden element that contains the lift instruction (nicer screen reader text)
  'aria-labelledby': ElementId,

  // Allow tabbing to this element
  tabIndex: number,

  // Stop html5 drag and drop
  draggable: boolean,
  onDragStart: (event: DragEvent) => void,
|};

I want to attach onDragStart to the Draggable so that it is fired only when a particular set of elements is dragged (as opposed to attaching it to DragDropContext). Using the example from the docs I do:

<Draggable draggableId="draggable-1" index={0}>
  {(provided, snapshot) => (
    <div
      ref={provided.innerRef}
      {...provided.draggableProps}
      {...provided.dragHandleProps}
      onDragStart={() => console.log('onDragStart')}
    >
      Drag me!
    </div>
  )}
</Draggable>

however it doesn't seem to be working. Any ideas how to make it work?

Upvotes: 7

Views: 10800

Answers (3)

Duda
Duda

Reputation: 1683

If you want to achieve this kind of behavior, you can wrap the react-beautiful-dnd context provider with your context provider, then use a hook to register the events you want in every component.

Context Provider - needs to wrap the app:

import React from 'react';
import {
  DragDropContext
} from 'react-beautiful-dnd';

export const MyDragDropContext = React.createContext({});

export const DragAndDropContext = ({
  children
}) => {
  const [eventsMap] = React.useState(() => new Map());

  const addEvent = (event, callback) => {
    eventsMap.set(event, callback);
  };

  const emitEvent = (event, ...rest) => {
    if (!eventsMap.has(event)) return;
    eventsMap.get(event)(...rest);
  };

  const removeEvent = (event) => {
    eventsMap.delete(event);
  };

  const handleDragEnd = (item) => {
    if (!item.destination) return;
    emitEvent(item.destination.droppableId, {
      action: 'DRAG_END',
      item
    });
  };

  const handleDragStart = (item) => {
    if (!item.source) return;
    emitEvent(item.source.droppableId, {
      action: 'DRAG_START',
      item
    });
  };

  return ( <
    MyDragDropContext.Provider value = {{addEvent,removeEvent}} >
      <DragDropContext 
        onDragStart = {handleDragStart}
        onDragEnd = {handleDragEnd} 
      > 
        {children} 
      </DragDropContext> 
    </MyDragDropContext.Provider >
  );
};

Hook:

import React, {
  useRef
} from 'react';
import {
  MyDragDropContext
} from './DragAndDropContext';

export interface DragMonitorListener {
  onDragStart(item) : void;
  onDragEnd(item) : void;
}

export default function useDragMonitor(droppableId, listener: DragMonitorListener) {
  const {
    addEvent,
    removeEvent
  } = React.useContext < any > (MyDragDropContext);
  const savedListener = useRef < DragMonitorListener > ();

  React.useEffect(() => {
    savedListener.current = listener;
    addEvent(droppableId, ({
      action,
      item
    }) => {
      if (action === 'DRAG_START') {
        listener.onDragStart(item);
      } else if (action === 'DRAG_END') {
        listener.onDragEnd(item);
      }
    });
    return () => removeEvent(droppableId);
  }, []);
}

To get the callbacks in your component you need to register with the droppable id:

useDragMonitor('droppable-id', {
  onDragStart(item) {
    console.log('onDragStart', item);
  },
  onDragEnd(item) {
    console.log('onDragEnd', item);
  },
});

Upvotes: 0

Mohamed Magdy
Mohamed Magdy

Reputation: 605

onDragStart and onDragEnd are events for DragDropContext component only.

Upvotes: 1

Sharique Khan
Sharique Khan

Reputation: 182

It can only be used only in DragDropContext for the Draggable element.

<DragDropContext
         onDragStart={()=>{alert('onDragStart')}}
         onDragEnd={()=>{alert('onDragEnd')}}>
</DragDropContext>

Upvotes: 6

Related Questions