React-beautiful-dnd is a powerful drag-and-drop package that can gracefully create rich drag-and-drop page applications. It is suitable for drag-and-drop scenarios between lists. It is mobile and easy to use. The official rich case show, just right.

The core concept

DragDropContext

import { DragDropContext } from 'react-beautiful-dnd';

const App = () = > {
  const onDragStart = () = > {
    / *... * /
  };
  const onDragUpdate = () = > {
    / *... * /
  };
  const onDragEnd = () = > {
    // the only one that is required
  };

  return (
    <DragDropContext
      onDragStart={onDragStart}
      onDragUpdate={onDragUpdate}
      onDragEnd={onDragEnd}
    >
      <div>Hello world</div>
    </DragDropContext>
  );
};
Copy the code

Build a dragable scope and place the React code you want to be able to drag and drop into the DragDropContext.

Supported events:

  • OnDragStart: Drag to start the callback

  • OnDragUpdate: Callback in drag

  • OnDragEnd: Callback at the end of drag

    const onDragEnd = (result) = > {
      console.log(result);
      /* {draggableId: "item-3", // from here source:{droppableId: "droppable", index: 2}, // move to here destination:{droppableId: "droppableId ", index: 2}, // move to here destination:{droppableId: "droppable", index: 1 } } */
    };
    Copy the code

WARNING: DragDropContext does not support nesting, and DranDropContext’s onDragEnd hook function must be set.

Droppable

import { Droppable } from 'react-beautiful-dnd';

<Droppable droppableId="droppable-1">
  {(provided, snapshot) => (
    <div
      ref={provided.innerRef}
      style={{ backgroundColor: snapshot.isDraggingOver ? 'blue' : 'grey'}} {. provided.droppableProps}
    >
      <h2>I am a droppable!</h2>
      {provided.placeholder}
    </div>
  )}
</Droppable>;
Copy the code

Build an area block that can be dragged and dropped into.

Parameter Description:

  • DroppableId: This property is required for unique identification, do not change this ID.
  • direction:vertical(Vertical drag, default) /horizontal(Horizontal drag)
  • type: Specifies the element class that can be dragged

The React child of Droppable must be a function that returns ReactElement. This function provides two parameters: provided and snapshot:

  • Provided. InnerRef: Must bind to the highest DOM node in the ReactElement.

  • Provided. DroppableProps: Object, which contains properties to be applied to the Droppable element and contains a data property that can be used to control some invisible CSS

    console.log(provided.droppableProps);
    
    /* { data-rbd-droppable-context-id: "1", data-rbd-droppable-id: "droppable" } */
    Copy the code
  • Provided. Placeholder: placeholder

  • Snapshot: The current drag state, which can be used to change the appearance of the Droppable while being dragged.

    console.log(snapshot);
    /* {draggingFromThisWith: null, draggingOverWith: null, isDraggingOver: false, isUsingPlaceholder: false} */
    Copy the code

Draggalbe

import { Draggable } from 'react-beautiful-dnd';

<Draggable draggableId="draggable-1" index={0}>
  {(provided, snapshot) => (
    <div
      ref={provided.innerRef}
      {. provided.draggableProps}
      {. provided.dragHandleProps}
    >
      <h4>My draggable</h4>
    </div>
  )}
</Draggable>;
Copy the code

Draggable elements,
must always be included in
and can be reordered within
or moved to another
.

Parameter Description:

  • draggableId:string, required, as a unique identifier.
  • index:numberMatching,DroppableDraggableThe order. Is in the listDraggableThe index array must be contiguous, for example[0].
  • isDragDisabled: the defaultfalse, an optional flag that controls whether permission is allowedDraggableDrag.
  • With other<Droppable />.

Start project

Effect preview:

The installation

# yarn
yarn add react-beautiful-dnd

# npm
npm install react-beautiful-dnd --save
Copy the code

Use the react – beautiful – DND:

import { DragDropContext } from 'react-beautiful-dnd';
Copy the code

directory

└ ─ ─ dndPro ├ ─ ─ component ├ ─ ─ the Column. The js ├ ─ ─ Item. Js ├ ─ ─ ItemList. Js └ ─ ─ Row. Js ├ ─ ─ the get - initial - data. Js / / initial data ├ ─ ─ Style.css // Style file ├ ─ index.jsCopy the code

Dom structure

We implement three drag-and-drop requirements: container-able drag-and-drop, element-traversable container drag-and-drop, and vertical drag-and-drop of elements inside containers. We will drag the
and virtual list mode implementation.

Layer 1: Containers can be dragged

wraps around the outermost layer, building a dragable range; Add the first
, a region block that can be dragged into the container, and specify the drag direction to be horizontal to implement the container drag type column (only className=’column’ elements can be dragged). Render two column components according to columnOrder: [‘column-0’, ‘column-1’].

// index.js

import { DragDropContext, Droppable } from 'react-beautiful-dnd';

<DragDropContext onDragEnd={onDragEnd}>
  <div className="dnd-pro">
    <Droppable
      droppableId="all-droppables"
      direction="horizontal"
      type="column"
    >
      {(provided) => (
        <div
          className="columns"
          {. provided.droppableProps}
          ref={provided.innerRef}
        >
          {state.columnOrder.map((columnId, index) => (
            <Column
              key={columnId}
              column={state.columns[columnId]}
              index={index}
            />
          ))}
          {provided.placeholder}
        </div>
      )}
    </Droppable>
  </div>
</DragDropContext>;
Copy the code

The Column component is a
element, which implements container-drag

// Column.js

import { Draggable } from 'react-beautiful-dnd';

<Draggable draggableId={column.id} index={index}>
  {(provided) => (
    <div
      className="column"
      {. provided.draggableProps}
      ref={provided.innerRef}
    >
      <h3 className="column-title" {. provided.dragHandleProps} >
        {column.title}
      </h3>
      <ItemList column={column} index={index} />
    </div>
  )}
</Draggable>;
Copy the code

Layer 2: Virtual list

React-beautiful-dnd supports drag and drop within and between virtual lists. In general, the recommended list size should not exceed 500 elements. Virtual list is a kind of window performance optimization, detailed introduction can be seen here, let’s directly see the use of it:

  • Virtual lists behave differently from regular lists. We need to tell RBD that our list is a virtual list.

    <Droppable droppableId="droppable" mode="virtual">
      {/ *... * /}
    </Droppable>
    Copy the code
  • Drag a copy of the project

    <Droppable
      droppableId="droppable"
      mode="virtual"
      renderClone={(provided, snapshot, rubric) = > (
        <div
          {. provided.draggableProps}
          {. provided.dragHandleProps}
          ref={provided.innerRef}
        >
          Item id: {items[rubric.source.index].id}
        </div>
      )}
    >
      {/ *... * /}
    </Droppable>
    Copy the code

One thing to note is that placeholder is having problems with the virtual list. Since the length of the virtual list no longer depends on the collective length of the element, but on the computational visual length, we need to do some processing:

const itemCount = snapshot.isUsingPlaceholder
  ? column.items.length + 1
  : column.items.length;
Copy the code

With a basic understanding of virtual lists, let’s return to
.

Create a new area block (
) that can be dragged vertically (default), specify virtual list mode (mode=”virtual”), and use renderClone when dragged.

// ItemList.js

import { FixedSizeList } from 'react-window';
import { Droppable } from 'react-beautiful-dnd';

<Droppable
  droppableId={column.id}
  mode="virtual"
  renderClone={(provided, snapshot.rubric) = > (
    <Item
      provided={provided}
      isDragging={snapshot.isDragging}
      item={column.items[rubric.source.index]}
    />
  )}
>
  {(provided, snapshot) => {
    const itemCount = snapshot.isUsingPlaceholder
      ? column.items.length + 1
      : column.items.length;

    return (
      <FixedSizeList
        height={500}
        itemCount={itemCount}
        itemSize={80}
        width={300}
        outerRef={provided.innerRef}
        itemData={column.items}
        className="task-list"
        ref={listRef}
      >
        {Row}
      </FixedSizeList>
    );
  }}
</Droppable>;
Copy the code

The
component and the
component are both Draggable elements.

onDragEnd

Container-level drags and container-level drags can be done by simply swapping elements:

// reordering list
if (result.type === 'column') {
  constcolumnOrder = reorderList( state.columnOrder, result.source.index, result.destination.index, ); setState({ ... state, columnOrder, });return;
}

// reordering in same list
if (result.source.droppableId === result.destination.droppableId) {
  const column = state.columns[result.source.droppableId];
  const items = reorderList(
    column.items,
    result.source.index,
    result.destination.index,
  );

  constnewState = { ... state,columns: {... state.columns, [column.id]: { ... column, items, }, }, }; setState(newState);return;
}
Copy the code

Elements are dragged across containers in two steps:

  • Removes elements from the source list
  • Add elements to the target list (Destination)
// moving between lists
const sourceColumn = state.columns[result.source.droppableId];
const destinationColumn = state.columns[result.destination.droppableId];
const item = sourceColumn.items[result.source.index];

// 1. remove item from source column
constnewSourceColumn = { ... sourceColumn,items: [...sourceColumn.items],
};
newSourceColumn.items.splice(result.source.index, 1);

// 2. insert into destination column
constnewDestinationColumn = { ... destinationColumn,items: [...destinationColumn.items],
};
newDestinationColumn.items.splice(result.destination.index, 0, item);

constnewState = { ... state,columns: {
    ...state.columns,
    [newSourceColumn.id]: newSourceColumn,
    [newDestinationColumn.id]: newDestinationColumn,
  },
};

setState(newState);
Copy the code

React Best Practices

  • React Best practice: Create a drag-and-drop list by hand
  • React Best Practice: Integrating third-party Libraries (d3.js)
  • React Best Practices: How to Implement Native Portals
  • React best practice: Drag-and-drop sidebar
  • React best practice: Implement step-by-step operations based on routes
  • React best practice: Work with multiple data sources
  • React best practice: Complete a list of requirements
  • React best practice: Dynamic forms