Drag and Drop Rows between Grids with the Common Drag&Drop Hooks
Environment
| Product Version | 16.0.0 |
| Product | Progress® KendoReact Grid |
Description
You want to drag and drop rows between two separate KendoReact Grid instances. The built-in rowReorderable prop only supports reordering within a single Grid because its internal drag engine validates that the drag source and drop target belong to the same Grid <tbody>—drops between two different Grids never fire.
This knowledge base article also answers the following questions:
- How to implement cross-Grid drag and drop in KendoReact?
- Why does
rowReorderablenot work when dragging a row to a different Grid? - How to use
useDraggableanduseDroppablewith the Gridrowsprop? - How to isolate a drag-and-drop context so it does not interfere with other Drag&Drop functionality on the page?
- How to show a custom drag hint that follows the cursor across both Grids?
Solution
Implement a single custom row component using the useDraggable and useDroppable hooks from the @progress/kendo-react-common package, and attach it to both Grids through the rows.data option.
- Create an isolated
React.createContext({})instance and pass it as thecontextoption touseDraggable,useDroppable, and theDragAndDropcomponent. This scopes the drag-and-drop state to these two Grids only, so it does not interfere with any other Drag&Drop functionality that might exist elsewhere on the page. - Wrap both Grids in a single shared
DragAndDropcomponent that uses the isolated context—this is required for theuseDroppablehook to detect a valid drop. - Define the custom row once, at module level, and reuse it for both Grids. Each row reads which Grid it belongs to from a small
GridIdContextinstead of receiving it through props, so the row component keeps a stable identity and is not recreated on every render. - Spread the
trPropsobject that the Grid passes to the custom row onto the<tr>element—it carries the attributes (role,aria-rowindex, click handlers, and so on) that the Grid relies on for its own behavior. - Track the item and Grid currently being dragged in refs (not state) inside a
TransferContext, so the drag callbacks always read the latest values without triggering extra renders while dragging. - Render a single fixed-position element as the drag
hintand pass its ref touseDraggable. The hint follows the pointer viaonDragand is shown only while a row is being dragged, giving the user a visual cue as they move a row from one Grid to the other. - On
drop, call a sharedmoveItemfunction that removes the item from its source Grid's data and adds it to the target Grid's data.
Remove
rowReorderablefrom both Grids when combining this pattern with them—the two systems are independent, and combining them leads to conflicting drag event listeners.
The following example demonstrates dragging a row from one Grid and dropping it onto a row of the other Grid to move the corresponding data item between the two data sources.