Reconciliation

How Pragmatic drag and drop reconciles pieces.

Drop targets

The key for a drop target is the element. At any one time, only a single drop target for a particular entity type (eg files) can be attached to the same element.

// ✅ Using the same element as a drop target for elements and for files
const cleanup = combine(
    dropTargetForElements({
        element: myElement,
    }),
    dropTargetForExternal({
        element: myElement,
    }),
);
// ❌ Using the same element for two drop targets of the same entity type is not allowed
const cleanup = combine(
    dropTargetForElements({
        element: myElement,
    }),
    // ⚠️ A warning will be logged if this is detected
    dropTargetForElements({
        element: myElement,
    }),
);

During a drag operation, if a drop target is removed and readded on the same element, then @atlaskit/pragmatic-drag-and-drop will treat the added drop target as if it was the same drop target.

const monitor = monitorForElements({
    onDragStart: () => console.log('monitor:start'),
    onDropTargetChange: () => console.log('monitor:change'),
    onDrop: () => console.log('monitor:drop'),
});
const cleanup1 = dropTargetForElements({
    element: A,
    onDragStart: () => console.log('A1:start'),
    onDropTargetChange: () => console.log('A1:change'),
    onDrop: () => console.log('A1:drop'),
});

// a drag starts
// console.log → 'A1:start', 'monitor:start'

// drop target is removed during a drag
cleanup1();
// console.log not called (no change event is fired)

const cleanup2 = dropTargetForElements({
    element: A,
    onDragStart: () => console.log('A2:start'),
    onDropTargetChange: () => console.log('A2:change'),
    onDrop: () => console.log('A2:drop'),
});

// a dragover occurs
// console.log → 'A2:drag', 'monitor:drag'
// 👆 note: no 'change' event occured, A2 is treated as A1

Draggables

The key for a draggable is the element. An element can only be used for a single draggable.

// ❌ Using the same element for two draggables
  draggable({
    element: myElement,
  }),
  // ⚠️ A warning will be logged if this is detected
  draggable({
    element: myElement,
  }),
);

During a drag operation, if the dragging draggable is removed and readded on the same element, then @atlaskit/pragmatic-drag-and-drop will treat the added draggable as if it was the original draggable.

const monitor = monitorForElements({
    onDragStart: () => console.log('monitor:start'),
    onDrop: () => console.log('monitor:drop'),
});
const cleanup1 = draggable({
    element: A,
    onDragStart: () => console.log('A1:start'),
    onDrop: () => console.log('A1:drop'),
});

// a drag starts
// console.log → 'A1:start', 'monitor:start'

// drop target is removed during a drag
cleanup1();
// console.log not called (no change event is fired)

const cleanup2 = dropTargetForElements({
    element: A,
    onDragStart: () => console.log('A2:start'),
    onDrop: () => console.log('A2:drop'),
});

// a drop occurs
// console.log → 'A2:drop', 'monitor:drop'
// 👆 note: the drop event occured on 'A2' even though it was not the same `draggable()` as the original drag

Remounting a draggable common in react where you might do something like this:

function Card() {
    const ref = useRef();
    const [dragCount, setCount] = useState(0);

    useEffect(() => {
        return draggable({
            element,
            onDragStart: () => setCount(dragCount + 1),
        });
        // when dragCount changes our `draggable` will be remounted
    }, [dragCount]);

    return <div ref={ref}>I have been dragged {dragCount} times</div>;
}

This is a contrived example, because you could avoid the remounting in this case by doing this:

function Card() {
    const ref = useRef();
    const [dragCount, setCount] = useState(0);

    useEffect(() => {
        return draggable({
            element,
            onDragStart: () => setCount((current) => current + 1),
        });
        // no longer need to remount when `dragCount` changes
    }, []);

    return <div ref={ref}>I have been dragged {dragCount} times</div>;
}

Monitors

Each call to create a monitor will create a new monitor, even if the monitor definition is shared. There is no key for monitors.

import { monitorForElements } from '@atlaskit/pragmatic-drag-and-drop/element/adapter';

const args = {
    onGenerateDragPreview: () => console.log('monitor:preview'),
};

// these two monitors are sharing the same `args` reference
// but they will both create an independent monitor
const cleanup1 = monitorForElements(args);
const cleanup2 = monitorForElements(args);

// → A drag starts

// console.log:
// - 'monitor:preview'
// - 'monitor:preview'

// → A drag finishes

// removing one monitor
cleanup1();

// → Another drag starts
// console.log:
// - 'monitor:preview'
// 👆 one monitor is still active

Was this page helpful?
We use this feedback to improve our documentation.