最近项目里有拖拽排序的需求,总结了使用TS的例子,代码如下
Contaier.tsx 容器组建
import update from 'immutability-helper'
import type { FC } from 'react'
import { useCallback, useState } from 'react'
import { Card } from './Card'
const style = {
width: 400,
}
export interface Item {
id: number
text: string
}
export interface ContainerState {
cards: Item[]
}
export const Container: FC = () => {
const [cards, setCards] = useState([
{
id: 1,
text: 'Write a cool JS library',
},
{
id: 2,
text: 'Make it generic enough',
},
{
id: 3,
text: 'Write README',
},
{
id: 4,
text: 'Create some examples',
},
{
id: 5,
text: 'Spam in Twitter and IRC to promote it (note that this element is taller than the others)',
},
{
id: 6,
text: '???',
},
{
id: 7,
text: 'PROFIT',
},
])
const moveCard = useCallback((dragIndex: number, hoverIndex: number) => {
setCards((prevCards: Item[]) =>
update(prevCards, {
$splice: [
[dragIndex, 1],
[hoverIndex, 0, prevCards[dragIndex] as Item],
],
}),
)
}, [])
const renderCard = useCallback(
(card: { id: number; text: string }, index: number) => {
return (
<Card
key={card.id}
index={index}
id={card.id}
text={card.text}
moveCard={moveCard}
/>
)
},
[],
)
return (
<>
<div style={style}>{cards.map((card, i) => renderCard(card, i))}</div>
</>
)
}
2、子移动组建 card.tsx
import type { Identifier, XYCoord } from 'dnd-core'
import type { FC } from 'react'
import { useRef } from 'react'
import { useDrag, useDrop } from 'react-dnd'
import { ItemTypes } from './ItemTypes'
const style = {
border: '1px dashed gray',
padding: '0.5rem 1rem',
marginBottom: '.5rem',
backgroundColor: 'white',
cursor: 'move',
}
export interface CardProps {
id: any
text: string
index: number
moveCard: (dragIndex: number, hoverIndex: number) => void
}
interface DragItem {
index: number
id: string
type: string
}
export const Card: FC<CardProps> = ({ id, text, index, moveCard }) => {
const ref = useRef<HTMLDivElement>(null)
const [{ handlerId }, drop] = useDrop<
DragItem,
void,
{ handlerId: Identifier | null }
>({
accept: ItemTypes.CARD,
collect(monitor) {
return {
handlerId: monitor.getHandlerId(),
}
},
hover(item: DragItem, monitor) {
if (!ref.current) {
return
}
const dragIndex = item.index
const hoverIndex = index
// Don't replace items with themselves
if (dragIndex === hoverIndex) {
return
}
// 确定屏幕上的矩形
const hoverBoundingRect = ref.current?.getBoundingClientRect()
// 得到垂直中间
const hoverMiddleY =
(hoverBoundingRect.bottom - hoverBoundingRect.top) / 2
// 确定鼠标位置
const clientOffset = monitor.getClientOffset()
// Get pixels to the top
const hoverClientY = (clientOffset as XYCoord).y - hoverBoundingRect.top
// 只有当鼠标越过物品高度的一半时才执行移动
// 向下拖动时,只有当光标低于50%时才移动
// 向上拖动时,只在光标高于50%时移动
// 向下拖动
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
return
}
// 向上拖动
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
return
}
moveCard(dragIndex, hoverIndex)
item.index = hoverIndex
},
})
const [{ isDragging }, drag] = useDrag({
type: ItemTypes.CARD,
item: () => {
return { id, index }
},
collect: (monitor: any) => ({
isDragging: monitor.isDragging(),
}),
})
const opacity = isDragging ? 0 : 1
drag(drop(ref))
return (
<div ref={ref} style={{ ...style, opacity }} data-handler-id={handlerId}>
{text}
</div>
)
}
效果图
项目完整代码:
csdn下载链接: link
提取码: 488r