介绍

react-beautiful-dnd 是一个 React 的拖拽库,它可以帮助你在应用中实现拖拽功能。

访问网站open in new window查看效果。

安装

npm install react-beautiful-dnd --save

使用

三个核心组件:

  • DragDropContext:拖拽上下文,用于包裹整个拖拽组件,只能有一个。
  • Droppable:可拖拽区域,用于包裹可拖拽的元素,可以有多个。
  • Draggable:可拖拽元素,用于包裹可拖拽的元素,可以有多个。

1.关闭 React 的严格模式

使用前需要关闭 React 的严格模式,因为 react-beautiful-dnd 会使用到一些不兼容严格模式的 API。

如何关闭:

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'

ReactDOM.createRoot(document.getElementById('root')).render(
  <App />
)

即去掉ReactStrictMode标签。

2.使用 App.jsx

import Task from './components/Task'
import { DragDropContext } from 'react-beautiful-dnd'
import { useState } from 'react'
import './App.less'

const tasks = [
  {
    id: '1',
    title: 'Estudar programação',
    status: 1
  },
  {
    id: '2',
    title: 'Ler livros',
    status: 0
  },
  {
    id: '3',
    title: 'Fazer compras',
    status: 2
  },
  {
    id: '4',
    title: 'Lavar o carro',
    status: 1
  }
]

function App() {
  const [taskList, setTaskList] = useState(tasks)

  const changeTaskStatus = (result) => {
    const statusMap = {
      'To-Do-Task': 0,
      Pending: 1,
      Completed: 2
    }
    const taskTitle = result.destination?.droppableId
    if (!taskTitle) return
    const status = statusMap[taskTitle]
    setTaskList((prevState) => {
      const task = prevState.find(item => item.id === result.draggableId)
      task.status = status
      return [...prevState]
    })
  }
  
  return (
    <DragDropContext
      onDragEnd={ result => changeTaskStatus(result) }
    >
      <div className="main">
        <Task title="To-Do-Task" tasks={ taskList.filter(item => item.status === 0) } />
        <Task title="Pending" tasks={ taskList.filter(item => item.status === 1) } />
        <Task title="Completed" tasks={ taskList.filter(item => item.status === 2) } />
      </div>
    </DragDropContext>
  )
}

export default App

3.使用 Task.jsx

import React from 'react'
import { Draggable, Droppable } from 'react-beautiful-dnd'
import './Task.less'

const Task = ({ tasks, title }) => {
  return (
    <div className="list-container">
      <span className="title">{ title }</span>
      <ul className="list-wrapper">
        <Droppable droppableId={ title }>
          {
            (provided, snapshot) => (
              <div
                ref={ provided.innerRef }
                { ...provided.droppableProps }
              >
                {
                  tasks.map((item, index) => {
                    // draggableId: 拖拽元素的唯一标识 必须是字符串
                    return <Draggable key={ item.id } draggableId={ item.id } index={ index }>
                      {
                        (provided, snapshot) => (
                          <div
                            { ...provided.dragHandleProps }
                            { ...provided.draggableProps }
                            ref={ provided.innerRef }
                          >
                            <li className="list-item">
                              <span className="item-title">{ item.title }</span>
                            </li>
                          </div>
                        )
                      }
                    </Draggable>
                  })
                }
                { provided.placeholder }
              </div>
            )
          }
        </Droppable>
      </ul>
    </div>
  )
}

export default Task

4.样式 App.less

.main {
  padding: 40px;
  display: flex;
  justify-content: center;
  gap: 20px;
}

5.样式 Task.less

.list-container {
  width: 100%;
  margin: 0;
  padding: 10px;
  max-width: 600px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-start;
  border: 1px solid #eee;

  .title {
    user-select: none;
  }

  .list-wrapper {
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    padding: 0 20px;

    .list-item {
      width: 100%;
      list-style: none;
      text-align: center;
      background-color: #f1c9c9;
      padding: 10px 0;
      cursor: pointer;
      margin-top: 10px;
      user-select: none;

      &:hover {
        background-color: #eee;
      }

      .list-item__text {
        font-size: 18px;
        font-weight: 500;
        color: #333;
      }

      .list-item__icon {
        font-size: 20px;
        color: #333;
      }
    }
  }
}
Last Updated:
Contributors: huangdingxin