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
Parameter description:
draggableId
:string
Must be used 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 to allowDraggable
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
│ ├── 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
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 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
// 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 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.