This is the 8th day of my participation in the August More text Challenge. For details, see: August More Text Challenge

React-beautiful-dnd, a powerful drag and drop package, can gracefully make rich drag and drop page applications, suitable for the list between the drag scene, support mobile terminal, and easy to use. The official case book, rich in examples, suits the mood.

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 drag-and-drop range and put 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 and drop

  • OnDragEnd: Callback at the end of drag

    const onDragEnd = (result) = > {
      console.log(result);
      Source :{droppableId: "droppable", index: 2} destination:{droppableId: "droppable", index: 2} destination:{droppableId: "droppable", index: 2} "droppable", index: 1 } } */
    };
    Copy the code

WARNING: DragDropContext does not support nesting, and the onDragEnd hook function of DranDropContext 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 a block of areas that can be dragged into.

Parameter description:

  • DroppableId: This attribute is required for unique identifiers. Do not change this ID.
  • direction:vertical(Drag vertically, default) /horizontal(Horizontal drag)
  • type: Specifies an element class that can be dragged

Droppable React subelements must be functions that return ReactElement. This function takes two arguments: provided and snapshot:

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

  • Provided. DroppableProps: Object. Contains attributes that need to be applied to Droppable elements. Contains a data attribute that you can use to control some invisible CSS elements

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

  • Snapshot: Current drag state, which can be used to change the Droppable’s appearance while being dragged.

    console.log(snapshot);
    /* {draggingFromThisWith: null, draggingOverWith: null, isDraggingOver: 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
. You can reorder
within
or move to another
.

Parameter description:

  • draggableId:stringMust be used 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 to allowDraggableDrag.
  • 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

│ ├── component ├─ class.js ├── item.js ├── itemlist.js ├── row.js ├── get-initial-data.js // ├─ Style.css // Style file ├ ─ index.jsCopy the code

Dom structure

There are three drag-and-drop requirements: container drag-and-drop, element drag-and-drop through the container, and vertical drag-and-drop inside the container. We will drag the direction
and virtual list pattern implementation.

Layer 1: Containers can be dragged

wraps around the outermost layer, creating a range that can be dragged; Add the first
, a block of areas that can be dragged into, and specify the drag direction as Horizontal to achieve container drag, specifying the drag type as 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. The Column component is a
element

// 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 lists

React-beautiful-dnd supports drag and drop within and between virtual lists. In general, the recommended list specification does not exceed 500 elements. Virtual list, is a window performance optimization, detailed introduction can see here, we directly to see how to use it:

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

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

    <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

Note that the placeholder has a problem with the virtual list. Since the length of the virtual list no longer depends on the collective length of the elements, but instead calculates the visual length, we need to do some processing:

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

Once you’ve gotten a feel for virtual lists, let’s go back to
.

Create a new block
that you can drag into. Drag vertically (default), specify the virtual list mode (“virtual”), and use renderClone when dragging occurs.

// 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 drag and drag with elements in the same container, simply by 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 the container in two steps:

  • Remove elements from the source list
  • Add the element 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

The complete code

Main logic and core code above, complete code stamp here.