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,
Parameter Description:
draggableId
:string
, required, as a unique identifier.index
:number
Matching,Droppable
中Draggable
The order. Is in the listDraggable
The index array must be contiguous, for example[0]
.isDragDisabled
: the defaultfalse
, an optional flag that controls whether permission is allowedDraggable
Drag.- 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
Layer 1: Containers can be dragged
// 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
// 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 (
// 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
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