Skip to main content
Version: v2.x

Callbacks & Monitoring

Drax has 19 callback events that cover the full drag-and-drop lifecycle. This guide walks through all three roles — dragged, receiver, and monitor — plus the continuous (per-frame) callbacks.

Dragged View Events​

Fired on the view being dragged:

#CallbackWhen
1onDragStartThe view starts being dragged
2onDragWhile dragged over no receiver (every frame)
3onDragEnterDragged into a receiver
4onDragOverWhile dragged over a receiver (every frame)
5onDragExitDragged out of a receiver
6onDragEndDrag ends outside a receiver (or is cancelled)
7onDragDropDrag ends successfully over a receiver
8onSnapEndSnap-back animation completes

Receiver View Events​

Fired on the view being dragged over:

#CallbackWhen
AonReceiveDragEnterA drag enters this view
BonReceiveDragOverWhile a drag is over this view (every frame)
ConReceiveDragExitA drag exits this view (or is cancelled)
DonReceiveDragDropA drag ends successfully over this view
EonReceiveSnapEndSnap animation to this view completes

Lifecycle Examples​

Example I — Drag, enter, exit​

  1. A DraxView is long-pressed → onDragStart (1)
  2. Dragged to the right → a series of onDrag (2) events
  3. Drag crosses into a receiver → onDragEnter (3) + onReceiveDragEnter (A)
  4. Continues through the receiver → onDragOver (4) + onReceiveDragOver (B) every frame
  5. Drag exits → onDragExit (5) + onReceiveDragExit (C)
  6. Released → onDragEnd (6)

Example II — Drag and drop​

Same flow, but released while over the receiver:

  • onDragDrop (7) on the dragged view
  • onReceiveDragDrop (D) on the receiver

Example III — Cancelled drag​

Drag is cancelled by the gesture system (no drop occurs):

  • onDragEnd (6) with cancelled: true
  • onReceiveDragExit (C) with cancelled: true

Continuous Callbacks​

Standard callbacks fire once per transition. Three continuous callbacks fire every gesture frame (~60fps):

CallbackFiresOn View
onDragEvery frame while over empty spaceDragged
onDragOverEvery frame while over the same receiverDragged
onReceiveDragOverEvery frame while something hoversReceiver
<DraxView
onDrag={(data) => {
// Every frame while dragging over empty space
console.log('Position:', data.dragAbsolutePosition);
}}
onDragOver={(data) => {
// Every frame while over a receiver
console.log('Over:', data.receiver.id);
}}
/>

<DraxView
onReceiveDragOver={(data) => {
const { receiveOffset } = data.receiver;
// Use position for visual feedback, cursor tracking, etc.
}}
/>
Performance

These fire every frame. Keep handlers lightweight — avoid state updates. Use SharedValue for UI-thread animations driven by continuous position data.

Monitoring​

For complex use cases, Drax has the concept of a monitoring view. A monitor observes drags happening inside it without the dragged view knowing.

Monitors vs Receivers​

AspectReceiverMonitor
Dragged view awarenessInformed via onDragOver etc.Dragged view is not informed
OverlapOnly the top-most receiver firesAny number of overlapping monitors fire
Start/end eventsNot receivedonMonitorDragStart, onMonitorDragEnd
Use caseDrop targetsCentralized drag tracking, sortable containers

When to Use Monitors​

Monitors track drags centrally. This is exactly how Drax's sortable architecture works internally:

  • SortableContainer is a monitoring DraxView — watches all drags to determine reorder positions
  • SortableBoardContainer monitors at the board level for cross-container detection

Monitor Events​

CallbackWhen
onMonitorDragStartA drag starts within this view
onMonitorDragEnterA drag enters this view
onMonitorDragOverWhile a drag is over this view (every frame)
onMonitorDragExitA drag exits this view
onMonitorDragEndDrag ends (no receiver / cancelled) within this view
onMonitorDragDropDrop occurs over a receiver within this view

Example: Full-Screen Monitor​

<DraxView
style={{ flex: 1 }}
monitoring
draggable={false}
receptive={false}
onMonitorDragStart={({ dragged }) => {
console.log(`Drag started: ${dragged.id}`);
}}
onMonitorDragOver={({ dragged, receiver, monitorOffset }) => {
console.log(`At offset: ${monitorOffset.x}, ${monitorOffset.y}`);
if (receiver) {
console.log(`Over receiver: ${receiver.id}`);
}
}}
onMonitorDragDrop={({ dragged, receiver }) => {
console.log(`Dropped ${dragged.id} onto ${receiver.id}`);
}}
>
<DraxView payload="item-1" style={styles.draggable}>
<Text>Drag me</Text>
</DraxView>
<DraxView onReceiveDragDrop={handleDrop} style={styles.receiver}>
<Text>Drop here</Text>
</DraxView>
</DraxView>

Monitor Event Data​

interface DraxMonitorEventData {
dragAbsolutePosition: Position;
dragTranslation: Position;
dragged: DraxEventDraggedViewData;
receiver?: DraxEventReceiverViewData; // present if over a receiver
monitorOffset: Position; // relative to monitor view
monitorOffsetRatio: Position; // ratio (0-1) within monitor
}

monitorOffset and monitorOffsetRatio are useful for:

  • Auto-scroll — detect when near edges (ratio > 0.9)
  • Position-based reorder — determine which slot the drag is over
  • Custom visual feedback — animate indicators based on position

Returning Snap Targets​

onDragEnd, onDragDrop, onReceiveDragDrop, onMonitorDragEnd, and onMonitorDragDrop can return a snap target:

<DraxView
onReceiveDragDrop={({ dragged, receiver }) =>
snapToAlignment(receiver.measurements, dragged.measurements, 'center')
}
/>

Return values:

  • Position — snap to that absolute coordinate
  • DraxSnapbackTargetPreset.None — no snap animation
  • DraxSnapbackTargetPreset.Default — default snap-back
  • undefined — default behavior

Next Steps​