Drag and Drop Tables with React-Beautiful-DND (Part I)
Tags: react, npm, ux, webdev
Published: 2019-11-16 02:40:51

This week, I wanted to experiment learning a new React component and implementing it into my [EffectiveDonate](http://effectivedonate.herokuapp.com) website. I began to think of what aspects of the site could use a cool new feature to improve its UX, and focused in on the Profile page. Previously, the Profile page allowed users to update their default themes (Health, Education, etc), and also to view the nonprofit projects that they had starred. The list of projects was organized in a [Semantic UI Table](https://react.semantic-ui.com/collections/table/), and enabled users to view key information about the projects, donate to the project, or delete the project from their stars. However, the table was sorted in chronological order, so that the user's most recent starred projects were all the way at the bottom of the table - not the best UX! 

While I could have easily just sorted the table in reverse chronological order as a quick fix, I wanted to give the user some more control. So I started to brainstorm some solutions in React to make the table more dynamic. I found this [List of Awesome React Components](https://github.com/brillout/awesome-react-components), and read through a list of several drag and drop components. Drag and drop would be a nice, clean way to let the user customize their starred projects! I eventually chose [React Beautiful DnD](https://github.com/atlassian/react-beautiful-dnd) - it had over 17k stars on GitHub, a nice instruction video and many examples.

![Original Profile Page](https://i.imgur.com/FlmF3l5.png)
*The original profile page, with starred projects table in chronological order* 

##What is React-Beautiful-DnD? 
React-Beautiful-DnD is a React package with a goal of creating drag and drop functionality for lists that anyone can use, even people who can't see. The main [design goal](https://medium.com/@alexandereardon/rethinking-drag-and-drop-d9f5770b4e6b) is physicality - they want users to feel like they are moving objects around by hand. It also has accessibility features, including drag and drop using just the keyboard. 

It also plays nicely with tables, specifically the Semantic UI React Table component, which sealed the deal for me to use it. 

##Implementing React-Beautiful-DnD on my Website
In order to make my `StarredProjectsList` component DnD-able, I followed a [video course](https://egghead.io/courses/beautiful-and-accessible-drag-and-drop-with-react-beautiful-dnd) on react-beautiful-dnd, and referenced this [example](https://github.com/Frankcarvajal/rbdnd-suir-table/blob/master/src/components/SemanticUIDnDTable.js) of a Semantic UI table component. Also I made sure to install the package with: `npm install react-beautiful-dnd --save`. 

While I recommend going through the two resources I listed above to thoroughly understand the process for implementing the component in your project, I'll give a few highlights of key components in the API here: 

### DragDropContext
This [component](https://github.com/atlassian/react-beautiful-dnd/blob/master/docs/api/drag-drop-context.md) is required to specify which part of your React tree you want to be able to use drag and drop. For me, I wrapped my entire Semantic UI `Table` component with `<DragDropContext />`. A required prop for this component is `onDragEnd`, a function that dictates how the list or table's state should change once the drag operation is complete. The opening tag for my `DragDropContext` is the following: `<DragDropContext onDragEnd={this.onDragEnd}>`. 

The `onDragEnd` method finds the index of the starred project I dragged, and splices it into the array of my `starredProjects` state. Below is my implementation of the method: 

``` javascript
  onDragEnd = result => {
    const { destination, source, reason } = result;

    // Not a thing to do...
    if (!destination || reason === 'CANCEL') {
      this.setState({
        draggingRowId: null,
      });
      return;
    }

    if (
      destination.droppableId === source.droppableId &&
      destination.index === source.index
    ) {
      return;
    }

    const starredProjects = Object.assign([], this.state.starredProjects);
    const project = this.state.starredProjects[source.index];
    starredProjects.splice(source.index, 1);
    starredProjects.splice(destination.index, 0, project);
    this.setState({
      starredProjects
    });
  }
```

### Droppable
A [`<Droppable/>`](https://github.com/atlassian/react-beautiful-dnd/blob/master/docs/api/droppable.md) is a container for `</Draggable/>` items. It can be dropped on by `</Draggable />`s. 

The only required prop for `<Droppable />`s is a string, `droppableId`. I wrapped my `<Table.Body/>` in the `<Droppable />` component, since that is the container of data on which I will be dragging rows. 

### Draggable
A [`<Draggable />`](https://github.com/atlassian/react-beautiful-dnd/blob/master/docs/api/draggable.md) is the React component that will actually be dragged around onto `<Droppable />`s. It must always be contained by a `<Droppable />`, but it can also be moved onto other `<Droppable />`s. 

The required props for `<Draggable />`s are: `draggableId` and `index`. Some important notes on these props: 

1) the `draggableId` *must* be a string. I initially made mine an integer and was stumped when my table rows couldn't be dragged. But once I added the `.toString()` function to the prop, it was all good. 
2) the `index` prop must be a consecutive integer `[1,2,3,etc]`. It also must be unique in each `<Droppable />`. 

Below is a snippet of my code where I wrap each `<Table.Row>` in a `<Droppable/>` after `map`ing each of the starred projects in state:

``` javascript
{this.state.starredProjects.map((project, idx) => {
 return (
     <Draggable
        draggableId={project.id.toString()}
        index={idx}
        key={project.id}
     >
       {(provided, snapshot) => (
       <Ref innerRef={provided.innerRef}>
         <Table.Row
     ...
```


##Children Function
Another quirk about the `<Droppable />` and `<Draggable />` components is that  their `React` child must be a function that requires a `ReactNode`. If this child function is not created, the component will error out.  The function contains two arguments: `provided` and `snapshot`. I recommend reading the documentation for both [`<Draggable />`](https://github.com/atlassian/react-beautiful-dnd/blob/master/docs/api/draggable.md) and [`<Droppable />`](https://github.com/atlassian/react-beautiful-dnd/blob/master/docs/api/droppable.md) to fully understand what these two arguments do and what props they take.

Also, the `<Draggable />` and `<Droppable />` components require an `HTMLElement` to be provided to them. This element can be created using the `ref` callback in React or the ['Ref'](https://bit.dev/semantic-org/semantic-ui-react/ref) Semantic UI Component. This [react-beautiful-dnd guide](https://github.com/atlassian/react-beautiful-dnd/blob/master/docs/guides/using-inner-ref.md) does a good job of explaining the purpose of the `ref` callback and how to avoid any errors. 

For an example of how I used the `provided` and `snapshot` arguments of the child function, as well as the `Ref` Semantic UI Component in my table, here is a snippet of the `<Droppable />` tag: 

``` javascript
<Droppable droppableId="table">
    {(provided, snapshot) => (
       <Ref innerRef={provided.innerRef}>
          <Table.Body {...provided.droppableProps}>
          ...
```

![GIF of the working DnD Table](https://media.giphy.com/media/H1vU3P0uSo4isgRKtb/giphy.gif)
*The working DnD table*

## Conclusion 
Overall, it was a fun and informative process to implement my Semantic UI Table with react-beautiful-dnd. I enjoyed learning the component's API and it was interesting to work with concepts that were new to me, like the children functions and `ref` callbacks. 

I definitely recommend viewing the [video course](https://egghead.io/courses/beautiful-and-accessible-drag-and-drop-with-react-beautiful-dnd) on react-beautiful-dnd, and also checking out the example code online. You can also reference my [table component file](https://github.com/milandhar/mod5-project-frontend/blob/master/src/components/StarredProjectsList.js) on GitHub to fully see how I implemented the DnD components. 

While I am satisfied with the UX that is available on the table component now, the next step is to make it persist on the backend so that when the user refreshes the page, the table re-renders in the new order. This should require a bit of creative manipulation on the backend, which I am excited to tackle next week :) 

Thank you for reading and let me know if you have any questions or comments!