使用react-dnd編寫一個可拖拽排列的list
今天的需求來啦:做一個可以通過拖拽排列的list。最終效果是這樣:
開始之前,我找了個市面上比較主流的工具庫,做個比對:
這個是atlassian用來開發(fā)jira看板功能的庫,有動畫效果,很漂亮,確實對得起這個包的名字。優(yōu)點是相對易用,而且好看,缺點是僅適合開發(fā)list一類的組件,而且?guī)啄昵熬鸵呀?jīng)不再維護了,對于新版的函數(shù)式react可能不太兼容。
- dnd-kit這個是atlassian在棄用
react-beautiful-dnd
后重新維護的一個版本,效果是一樣的美麗,如果是web端的,還是很推薦的。 - reat-dnd這是個比較全面的dnd庫,可以實現(xiàn)各種功能,要注意的是這是基于H5的開發(fā),如果你開發(fā)的端不是H5,可能不適用。
react-dnd
的官方文檔介紹了如何做一個棋盤,類似的,這篇文章將介紹如何用react-dnd
做一個draggable
list。
Draggable
首先將list中的每一個item做成draggable
組件。
引入useDrag
:
// ListItem.tsx import { styled } from '@mui/material/styles'; import * as React from 'react'; import { useDrag } from 'react-dnd'; const StyledListItem = styled('div', { shouldForwardProp: prop => prop !== 'isDragging' })<{ isDragging: boolean }>(({ isDragging, theme }) => ({ opacity: isDragging ? 0.5 : 1, border: '1px blue solid', })); const ListItem = () => { const [{isDragging}, dragRef] = useDrag(() => ({ type: 'listItem', collect: monitor => ({ isDragging: !!monitor.isDragging(), }), })) return ( <StyledListItem ref={dragRef} isDragging={isDragging}> list item </StyledListItem> ) } export default ListItem;
這樣我們在demo中就能看到,這個組件就可以用鼠標(biāo)拖了:
Droppable
我們再將list容器做成droppable
組件,這樣draggable
組件才可以放在droppable
的list里。
引入useDrop
:
// SortableList.tsx import React, { forwardRef } from 'react'; import { useDrop } from 'react-dnd'; import ListItem from './components/listItem'; const SortableList = () => { const canMoveItem = () => { console.log('canMoveItem') return true; //<--- 具體邏輯后面再寫,這里先設(shè)為true,即容器內(nèi)部一直是可以drop的。 } const moveItem = () => { console.log('moveItem'); //<--- 具體邏輯后面再寫。 } const [{ isOver, canDrop }, dropRef] = useDrop( () => ({ accept: 'listItem', canDrop: () => canMoveItem(), drop: () => moveItem(), collect: (monitor) => ({ isOver: !!monitor.isOver(), canDrop: !!monitor.canDrop() }) }), [canMoveItem, moveItem] ) return ( <div> isOver: {String(isOver)}<br /> canDrop: {String(canDrop)} <div ref={dropRef} style={{ position: 'relative', width: '600px', height: '400px', border: '2px purple solid' }} > <ListItem></ListItem> </div> </div> ); } export default SortableList;
這樣頁面上,我再容器內(nèi)部拖動時,isOver
為true
,canDrop
為true
,拖到容器外isOver
就變成false
了:
既是draggable,又是droppable
當(dāng)有多個list item,可以通過拖拽調(diào)整順序,這樣我需要將list item也變成droppable
:
// ListItem.tsx // 將list item也變成droppable const [spec, dropRef] = useDrop({ accept: 'listItem', hover: (item, monitor) => { const dragIndex = item.index const hoverIndex = index const hoverBoundingRect = ref.current?.getBoundingClientRect() const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2 const hoverActualY = monitor.getClientOffset().y - hoverBoundingRect.top // 如果是向下拖,只有當(dāng)hover的坐標(biāo)小于自身高度的一半時,繼續(xù)保持drag,否則相當(dāng)于要挪動其他的list item,給當(dāng)前的item騰個地方 // if dragging down, continue only when hover is smaller than middle Y if (dragIndex < hoverIndex && hoverActualY < hoverMiddleY) return // 如果是向上拖,只有當(dāng)hover的坐標(biāo)大于自身高度的一半時,繼續(xù)保持drag,否則相當(dāng)于要挪動其他的list item,給當(dāng)前的item騰個地方 if (dragIndex > hoverIndex && hoverActualY > hoverMiddleY) return moveListItem(dragIndex, hoverIndex) item.index = hoverIndex }, });
那么如何讓一個list item既是draggable
,又是droppable
呢?用以下方式:
// ListItem.tsx const ref = useRef(null); const dragDropRef = dragRef(dropRef(ref))
重排list
上一步我們把moveListItem
傳給了父組件,那么在父組件中,我們可以這樣來改變list的順序:
// Mock data const PETS = [ { id: 1, name: 'dog' }, { id: 2, name: 'cat' }, { id: 3, name: 'fish' }, { id: 4, name: 'hamster' }, ]
// SortableList.tsx const moveListItem = useCallback( (dragIndex, hoverIndex) => { console.log({dragIndex, hoverIndex}) const dragItem = pets[dragIndex] const hoverItem = pets[hoverIndex] // 將PETS數(shù)組中,drag的item和hover的item交換位置 setPets(pets => { const updatedPets = [...pets] updatedPets[dragIndex] = hoverItem updatedPets[hoverIndex] = dragItem return updatedPets }) }, [pets], )
這樣我們就能得到這樣的效果:
這樣一個sortable list就大功告成啦!
總結(jié)
現(xiàn)在這個list是沒有任何動畫的,要是listItem
在移動的過程中是有個過渡效果的,就顯得絲滑多了,那么接下來我將用react-flip-move
將動畫效果加到每個item中。
到此這篇關(guān)于使用react-dnd編寫一個可拖拽排列的list的文章就介紹到這了,更多相關(guān)react-dnd可拖拽排列的list內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React動態(tài)更改html標(biāo)簽的實現(xiàn)方式
這篇文章主要介紹了React動態(tài)更改html標(biāo)簽的實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12React?useEffect異步操作常見問題小結(jié)
本文主要介紹了React?useEffect異步操作常見問題小結(jié),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06React組件、狀態(tài)管理、代碼優(yōu)化的技巧
文章總結(jié)了React組件設(shè)計、狀態(tài)管理、代碼組織和優(yōu)化的技巧,它涵蓋了使用Fragment、props解構(gòu)、defaultProps、key和ref的使用、渲染性能優(yōu)化等方面2024-11-11