Use material-ui table with react-beautiful-dnd
I came across this while working on a similar task. Re the OP's question, TableRow
and TableBody
both support a ref
prop (vs. innerRef
) that forwards to the root element.
Refer to the FAQ:
- https://material-ui.com/getting-started/faq/#how-can-i-access-the-dom-element
This is mentioned in the docs for both TableRow
and TableBody
:
- https://material-ui.com/api/table-row/
- https://material-ui.com/api/table-body/
Therefore you do not necessarily need to move the Draggable/Droppable parts into their own components per the answers from @Bryant and @funql.org.
You also don't necessarily need to pass isDragging
to your TableRow
(though you could if you created a custom wrapper component that accepts this prop). If your use-case is styling the row when dragged, you can add a className
or style
prop to your TableRow
and apply conditional styling when isDragging
is true
.
Here's a working example of a Material UI table that features sortable rows with react-beautiful-dnd:
- https://codesandbox.io/s/react-material-ui-and-react-beautiful-dnd-uofv4
Incidentally I created it as part of an issue report because I'm not sure why there is a 1 pixel jump (the thickness of the border-bottom on table cells) when a given table row is dragged, but that's another issue! The implementation is solid enough to support the many cases where a minor 1px visual glitch is acceptable.
I had this same problem. What you have to do is move the Draggable/Droppable part into a component and pass that in via the component attribute.
For example, I wanted to be able to re-order columns in a table header. Row is my droppable area, and Cell is Draggable.
public render() {
return (
<TableHead component="div">
<TableRow component={DroppableComponent(this.onDragEnd)}>{headerCells}</TableRow>
</TableHead>
);
}
const DroppableComponent = (
onDragEnd: (result: DropResult, provided: ResponderProvided) => void
) => (props: any) => {
return (
<DragDropContext onDragEnd={onDragEnd}>
<Droppable droppableId={'1'} direction="horizontal">
{(provided) => {
return (
<div ref={provided.innerRef} {...provided.droppableProps} {...props}>
{props.children}
{provided.placeholder}
</div>
);
}}
</Droppable>
</DragDropContext>
);
};
Notice I made Droppable Component be a function that returns a function. That was so that I could pass in the onDragEnd method from my main component. I tried putting my DroppableComponent in the component attribute as JSX and I was getting an error, so this is what I ended up with.
For the Draggable part I had the following:
<TableCell
component={DraggableComponent(column.id, index)}
key={column.id}
>
...
</TableCell>
const DraggableComponent = (id: string, index: number) => (props: any) => {
return (
<Draggable draggableId={id} index={index}>
{(provided) => (
<div
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
{...props}
>
{props.children}
</div>
)}
</Draggable>
);
};
Hope this helps!
I have created a github example repo using @Bryant's solution. https://github.com/hmarggraff/react-material-ui-table-row-drag-and-drop
It shows how to combine Bryants solution with react-material-ui. It also shows row vs colums dnd and visual feedback during dnd. It is a complete reusable solution.