React Native Reanimated DnDReact Native Reanimated DnD
API ReferenceContext & Providers

DragDropContext API

Complete API reference for the DragDropContext (exported as SlotsContext).

Import

import { SlotsContext } from "react-native-reanimated-dnd";

Context Value

SlotsContextValue Interface

interface SlotsContextValue<TData = unknown> {
  // Drop zone management
  register: (id: number, slot: DropSlot<TData>) => void;
  unregister: (id: number) => void;
  getSlots: () => Record<number, DropSlot<TData>>;
  isRegistered: (id: number) => boolean;

  // Active state management
  setActiveHoverSlot: (id: number | null) => void;
  activeHoverSlotId: number | null;

  // Position updates
  registerPositionUpdateListener: (
    id: string,
    listener: PositionUpdateListener
  ) => void;
  unregisterPositionUpdateListener: (id: string) => void;
  requestPositionUpdate: () => void;

  // Dropped items management
  registerDroppedItem: (
    draggableId: string,
    droppableId: string,
    itemData: any
  ) => void;
  unregisterDroppedItem: (draggableId: string) => void;
  getDroppedItems: () => DroppedItemsMap<any>;

  // Capacity management
  hasAvailableCapacity: (droppableId: string) => boolean;

  // Event callbacks
  onDragging?: (payload: DraggingPayload) => void;
  onDragStart?: (data: any) => void;
  onDragEnd?: (data: any) => void;
}

Methods

Drop Zone Management

register(id, slot)

Registers a new drop zone with the context.

  • Parameters:
    • id (number): Unique identifier for the drop zone
    • slot (DropSlot<TData>): Drop zone configuration object
  • Returns: void
  • Description: Adds a drop zone to the context's registry

unregister(id)

Removes a drop zone from the context.

  • Parameters:
    • id (number): Unique identifier of the drop zone to remove
  • Returns: void
  • Description: Removes a drop zone from the context's registry

getSlots()

Returns all currently registered drop zones.

  • Parameters: None
  • Returns: Record<number, DropSlot<TData>>
  • Description: Gets a mapping of all registered drop zones by their IDs

isRegistered(id)

Checks if a drop zone is currently registered.

  • Parameters:
    • id (number): Drop zone ID to check
  • Returns: boolean
  • Description: Returns true if the drop zone is registered

Active State Management

setActiveHoverSlot(id)

Sets the currently active (hovered) drop zone.

  • Parameters:
    • id (number | null): ID of the active drop zone, or null for none
  • Returns: void
  • Description: Updates which drop zone is currently being hovered over

activeHoverSlotId

The ID of the currently active drop zone.

  • Type: number | null
  • Description: Read-only property indicating which drop zone is active

Position Updates

registerPositionUpdateListener(id, listener)

Registers a listener for position updates.

  • Parameters:
    • id (string): Unique identifier for the listener
    • listener (PositionUpdateListener): Callback function to invoke on updates
  • Returns: void
  • Description: Adds a listener that will be called when positions need updating

unregisterPositionUpdateListener(id)

Removes a position update listener.

  • Parameters:
    • id (string): Unique identifier of the listener to remove
  • Returns: void
  • Description: Removes a previously registered position update listener

requestPositionUpdate()

Triggers position updates for all registered listeners.

  • Parameters: None
  • Returns: void
  • Description: Manually triggers position recalculation for all components

Dropped Items Management

registerDroppedItem(draggableId, droppableId, itemData)

Records that an item has been dropped in a specific zone.

  • Parameters:
    • draggableId (string): ID of the draggable item
    • droppableId (string): ID of the drop zone
    • itemData (any): Data associated with the dropped item
  • Returns: void
  • Description: Registers a successful drop operation

unregisterDroppedItem(draggableId)

Removes a dropped item from the registry.

  • Parameters:
    • draggableId (string): ID of the draggable item to remove
  • Returns: void
  • Description: Unregisters a previously dropped item

getDroppedItems()

Returns the current mapping of dropped items.

  • Parameters: None
  • Returns: DroppedItemsMap<any>
  • Description: Gets all currently dropped items and their locations

Capacity Management

hasAvailableCapacity(droppableId)

Checks if a drop zone has available capacity.

  • Parameters:
    • droppableId (string): ID of the drop zone to check
  • Returns: boolean
  • Description: Returns true if the drop zone can accept more items

Type Definitions

DropSlot

interface DropSlot<TData = unknown> {
  id: string;
  x: number;
  y: number;
  width: number;
  height: number;
  onDrop: (data: TData) => void;
  dropAlignment?: DropAlignment;
  dropOffset?: DropOffset;
  capacity?: number;
}

Properties

  • id: Unique string identifier for the drop zone
  • x: X coordinate of the drop zone
  • y: Y coordinate of the drop zone
  • width: Width of the drop zone
  • height: Height of the drop zone
  • onDrop: Callback function called when an item is dropped
  • dropAlignment: Optional alignment configuration for dropped items
  • dropOffset: Optional offset configuration for dropped items
  • capacity: Optional maximum number of items the zone can hold

DropAlignment

type DropAlignment =
  | "center"
  | "top-left"
  | "top-center"
  | "top-right"
  | "center-left"
  | "center-right"
  | "bottom-left"
  | "bottom-center"
  | "bottom-right";

DropOffset

interface DropOffset {
  x: number;
  y: number;
}

PositionUpdateListener

type PositionUpdateListener = () => void;

DroppedItemsMap

interface DroppedItemsMap<TData = unknown> {
  [draggableId: string]: {
    droppableId: string;
    data: TData;
  };
}

DraggingPayload

interface DraggingPayload {
  x: number; // Original X position
  y: number; // Original Y position
  tx: number; // Current X translation
  ty: number; // Current Y translation
  itemData: any; // Data associated with the draggable item
}

Usage Examples

Accessing the Context

import { useContext } from "react";
import { SlotsContext } from "react-native-reanimated-dnd";

function MyComponent() {
  const context = useContext(SlotsContext);

  if (!context) {
    throw new Error("Component must be used within a DropProvider");
  }

  return <View />;
}

Custom Hook

import { useContext } from "react";
import { SlotsContext } from "react-native-reanimated-dnd";

function useDragDropContext() {
  const context = useContext(SlotsContext);

  if (!context) {
    throw new Error("useDragDropContext must be used within a DropProvider");
  }

  return context;
}

Registering a Drop Zone

function CustomDroppable({ onDrop, children }) {
  const context = useDragDropContext();
  const viewRef = useRef<View>(null);
  const slotId = useRef(Math.random());

  useEffect(() => {
    const measureAndRegister = () => {
      viewRef.current?.measure((x, y, width, height, pageX, pageY) => {
        context.register(slotId.current, {
          id: "custom-drop-zone",
          x: pageX,
          y: pageY,
          width,
          height,
          onDrop,
          dropAlignment: "center",
          capacity: 5,
        });
      });
    };

    measureAndRegister();
    return () => context.unregister(slotId.current);
  }, [context, onDrop]);

  return (
    <View ref={viewRef} onLayout={measureAndRegister}>
      {children}
    </View>
  );
}

Monitoring Active State

function ActiveStateMonitor() {
  const { activeHoverSlotId, getSlots } = useDragDropContext();

  const activeSlot = useMemo(() => {
    if (!activeHoverSlotId) return null;
    const slots = getSlots();
    return slots[activeHoverSlotId] || null;
  }, [activeHoverSlotId, getSlots]);

  return (
    <View>
      {activeSlot ? (
        <Text>Active Zone: {activeSlot.id}</Text>
      ) : (
        <Text>No active zone</Text>
      )}
    </View>
  );
}

Position Update Listener

function ResponsiveComponent() {
  const { registerPositionUpdateListener, unregisterPositionUpdateListener } =
    useDragDropContext();
  const listenerId = useRef(`listener-${Math.random()}`);

  useEffect(() => {
    const handlePositionUpdate = () => {
      // Re-measure and update positions
      console.log("Position update triggered");
    };

    registerPositionUpdateListener(listenerId.current, handlePositionUpdate);

    return () => {
      unregisterPositionUpdateListener(listenerId.current);
    };
  }, [registerPositionUpdateListener, unregisterPositionUpdateListener]);

  return <View />;
}

Dropped Items Tracking

function DroppedItemsTracker() {
  const { getDroppedItems } = useDragDropContext();
  const [droppedItems, setDroppedItems] = useState({});

  useEffect(() => {
    const updateDroppedItems = () => {
      setDroppedItems(getDroppedItems());
    };

    const interval = setInterval(updateDroppedItems, 1000);
    return () => clearInterval(interval);
  }, [getDroppedItems]);

  return (
    <View>
      <Text>Dropped Items: {Object.keys(droppedItems).length}</Text>
      {Object.entries(droppedItems).map(([id, { droppableId, data }]) => (
        <Text key={id}>
          {data.name} → {droppableId}
        </Text>
      ))}
    </View>
  );
}

Capacity Checking

function CapacityChecker({ droppableId }) {
  const { hasAvailableCapacity } = useDragDropContext();
  const [canAcceptDrop, setCanAcceptDrop] = useState(true);

  useEffect(() => {
    const checkCapacity = () => {
      setCanAcceptDrop(hasAvailableCapacity(droppableId));
    };

    checkCapacity();
    const interval = setInterval(checkCapacity, 500);
    return () => clearInterval(interval);
  }, [droppableId, hasAvailableCapacity]);

  return (
    <View
      style={[
        styles.indicator,
        { backgroundColor: canAcceptDrop ? "green" : "red" },
      ]}
    >
      <Text>{canAcceptDrop ? "Available" : "Full"}</Text>
    </View>
  );
}

Error Handling

Context Validation

function validateContext(
  context: SlotsContextValue | null
): asserts context is SlotsContextValue {
  if (!context) {
    throw new Error(
      "DragDropContext not found. Make sure your component is wrapped in a DropProvider."
    );
  }
}

Safe Context Access

function SafeContextConsumer() {
  const context = useContext(SlotsContext);

  try {
    validateContext(context);

    // Safe to use context methods
    const slots = context.getSlots();

    return <View>{/* Your component */}</View>;
  } catch (error) {
    return (
      <View style={styles.errorContainer}>
        <Text style={styles.errorText}>{error.message}</Text>
      </View>
    );
  }
}

Performance Considerations

  • Context reads should be minimized and cached when possible
  • Position updates are automatically throttled
  • Listeners are cleaned up automatically on component unmount
  • Use useMemo and useCallback for expensive operations

TypeScript Support

The context is fully typed with generic support:

interface TaskData {
  id: string;
  title: string;
  priority: "low" | "medium" | "high";
}

function TypedComponent() {
  const context = useContext(SlotsContext) as SlotsContextValue<TaskData>;

  // All methods are properly typed
  const droppedItems = context.getDroppedItems();

  return <View />;
}

See Also