为了账号安全,请及时绑定邮箱和手机立即绑定

React-beautiful-dnd入门详解:打造优雅的拖拽交互体验

概述

React-beautiful-dnd 是一个流行的 React 拖拽库,提供了一组简洁的 API 来实现复杂的拖拽交互。它能够帮助开发者轻松地创建美观且功能强大的拖拽界面,广泛应用于项目管理、列表排序、文件拖拽等多种场景中。文章详细介绍了其安装、基本用法、高级用法以及常见问题的解决方案。

React-beautiful-dnd简介

React-beautiful-dnd 是一个流行的 React 拖拽库,它提供了一组简洁的 API 来实现复杂的拖拽交互。React-beautiful-dnd 能够帮助开发者轻松地创建美观且功能强大的拖拽界面,广泛应用于项目管理、列表排序、文件拖拽等多种场景中。

React-beautiful-dnd的主要应用场景

React-beautiful-dnd 主要应用于需要拖拽功能的交互场景中。常见的应用场景包括但不限于:

  • 项目管理:例如看板、任务列表等。
  • 列表排序:例如 UI 设计中的布局调整。
  • 文件拖拽:例如文件上传或文件管理。

安装React-beautiful-dnd

安装过程非常简单,可以通过 npm 或 yarn 来安装 React-beautiful-dnd。

使用npm

npm install react-beautiful-dnd

使用yarn

yarn add react-beautiful-dnd

安装完成后,你可以在项目中直接引用 react-beautiful-dnd 来实现拖拽功能。下面是一个简单的引入示例:

import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';

初识Draggable和Droppable

Draggable的基本用法

Draggable 是 React-beautiful-dnd 中用于标识可被拖拽元素的组件。它可以接收一个唯一的 draggableId,以及一个 type,并配合 Droppable 使用,来实现拖拽功能。

示例代码
import { Draggable } from 'react-beautiful-dnd';

const Item = ({ index, id }) => (
  <Draggable draggableId={id.toString()} index={index}>
    {(provided) => (
      <div
        ref={provided.innerRef}
        {...provided.draggableProps}
        {...provided.dragHandleProps}
      >
        Item {id}
      </div>
    )}
  </Draggable>
);

Droppable的基本用法

Droppable 是一个用于标识可接受拖拽元素的容器组件。它会接收一个 type,用于指定该容器接收哪个类型的 Draggable

示例代码
import { Droppable } from 'react-beautiful-dnd';

const List = ({ items }) => (
  <Droppable droppableId="1" type="item">
    {(provided) => (
      <div ref={provided.innerRef} {...provided.droppableProps}>
        {items.map((item, index) => (
          <Item key={item.id} index={index} id={item.id} />
        ))}
        {provided.placeholder}
      </div>
    )}
  </Droppable>
);

实战:创建一个简单的拖拽列表

通过结合 DraggableDroppable,我们可以创建一个简单的拖拽列表。下面是一个完整的拖拽列表实现:

示例代码
import React, { useState } from 'react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';

const Item = ({ id, index }) => {
  return (
    <Draggable draggableId={id.toString()} index={index}>
      {(provided) => (
        <div
          ref={provided.innerRef}
          {...provided.draggableProps}
          {...provided.dragHandleProps}
        >
          Item {id}
        </div>
      )}
    </Draggable>
  );
};

const List = ({ items }) => {
  return (
    <Droppable droppableId="1" type="item">
      {(provided) => (
        <div ref={provided.innerRef} {...provided.droppableProps}>
          {items.map((item, index) => (
            <Item key={item.id} id={item.id} index={index} />
          ))}
          {provided.placeholder}
        </div>
      )}
    </Droppable>
  );
};

const App = () => {
  const [items, setItems] = useState([{ id: 1 }, { id: 2 }, { id: 3 }]);

  return (
    <DragDropContext onDragEnd={() => {}}>
      <List items={items} />
    </DragDropContext>
  );
};

export default App;

使用收集器(useDroppable和useDraggable)

了解useDroppable和useDraggable

React-beautiful-dnd 提供了两个 Hook:useDraggableuseDroppable,用于获取拖拽和放置的状态信息。

  • useDraggable 是一个 Hook,用于获取拖拽元素的状态。
  • useDroppable 是一个 Hook,用于获取放置容器的状态。

通过收集器获取拖拽状态

通过 useDraggableuseDroppable,你可以获取到拖拽过程中的各种状态信息,例如是否正在被拖拽、当前拖拽的位置等。

示例代码
import { useDraggable, useDroppable } from 'react-beautiful-dnd';
import { useState } from 'react';

const Item = ({ id, index }) => {
  const { isDragging, draggableProps } = useDraggable({
    id: id.toString(),
    index,
  });

  return (
    <div
      ref={draggableProps.ref}
      {...draggableProps.dragHandleProps}
      style={isDragging ? { opacity: 0.5 } : {}}
    >
      Item {id}
    </div>
  );
};

const List = ({ items }) => {
  const { isOver, droppableProps, innerRef } = useDroppable({
    type: 'item',
    droppableId: '1',
  });

  return (
    <div ref={innerRef} {...droppableProps}>
      {items.map((item, index) => (
        <Item key={item.id} id={item.id} index={index} />
      ))}
    </div>
  );
};

const App = () => {
  const [items, setItems] = useState([{ id: 1 }, { id: 2 }, { id: 3 }]);

  return (
    <DragDropContext onDragEnd={() => {}}>
      <List items={items} />
    </DragDropContext>
  );
};

export default App;

实战:处理拖拽中的各种状态

你可以根据拖拽状态来改变元素的样式、内容,或者执行一些特定的逻辑。

示例代码
import React, { useState } from 'react';
import { DragDropContext, Droppable, Draggable, useDraggable, useDroppable } from 'react-beautiful-dnd';

const Item = ({ id, index }) => {
  const { isDragging, draggableProps, dragHandleProps } = useDraggable({
    id: id.toString(),
    index,
  });

  return (
    <Draggable draggableId={id.toString()} index={index}>
      {(provided) => (
        <div
          ref={provided.innerRef}
          {...draggableProps}
          {...dragHandleProps}
          style={isDragging ? { opacity: 0.5 } : {}}
        >
          Item {id}
        </div>
      )}
    </Draggable>
  );
};

const List = ({ items }) => {
  const { isOver, droppableProps, innerRef } = useDroppable({
    type: 'item',
    droppableId: '1',
  });

  return (
    <Droppable droppableId="1" type="item">
      {(provided) => (
        <div ref={innerRef} {...droppableProps} style={isOver ? { backgroundColor: 'lightgray' } : {}}>
          {items.map((item, index) => (
            <Item key={item.id} id={item.id} index={index} />
          ))}
          {provided.placeholder}
        </div>
      )}
    </DragDropContext>
  );
};

const App = () => {
  const [items, setItems] = useState([{ id: 1 }, { id: 2 }, { id: 3 }]);

  return (
    <DragDropContext onDragEnd={() => {}}>
      <List items={items} />
    </DragDropContext>
  );
};

export default App;

自定义样式与动画

如何自定义Draggable和Droppable的样式

你可以在 DraggableDroppable 的标签中添加自定义的 CSS 样式,来改变元素的外观。

示例代码
import React, { useState } from 'react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';

const Item = ({ id, index }) => (
  <Draggable draggableId={id.toString()} index={index}>
    {(provided) => (
      <div
        ref={provided.innerRef}
        {...provided.draggableProps}
        {...provided.dragHandleProps}
        style={{ padding: '10px', margin: '10px', backgroundColor: 'lightblue' }}
      >
        Item {id}
      </div>
    )}
  </Draggable>
);

const List = ({ items }) => (
  <Droppable droppableId="1" type="item">
    {(provided) => (
      <div ref={provided.innerRef} {...provided.droppableProps} style={{ padding: '10px', margin: '10px', backgroundColor: 'lightgray' }}>
        {items.map((item, index) => (
          <Item key={item.id} index={index} id={item.id} />
        ))}
        {provided.placeholder}
      </div>
    )}
  </DragDropContext>
);

const App = () => {
  const [items, setItems] = useState([{ id: 1 }, { id: 2 }, { id: 3 }]);

  return (
    <DragDropContext onDragEnd={() => {}}>
      <List items={items} />
    </DragDropContext>
  );
};

export default App;

添加拖拽动画

你可以通过 CSS 来实现拖拽时的动画效果。

示例代码
import React, { useState } from 'react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';

const Item = ({ id, index }) => (
  <Draggable draggableId={id.toString()} index={index}>
    {(provided) => (
      <div
        ref={provided.innerRef}
        {...provided.draggableProps}
        {...provided.dragHandleProps}
        style={{ padding: '10px', margin: '10px', backgroundColor: 'lightblue', transition: 'transform 0.3s ease' }}
      >
        Item {id}
      </div>
    )}
  </Draggable>
);

const List = ({ items }) => (
  <Droppable droppableId="1" type="item">
    {(provided) => (
      <div ref={provided.innerRef} {...provided.droppableProps} style={{ padding: '10px', margin: '10px', backgroundColor: 'lightgray' }}>
        {items.map((item, index) => (
          <Item key={item.id} index={index} id={item.id} />
        ))}
        {provided.placeholder}
      </div>
    )}
  </DragDropContext>
);

const App = () => {
  const [items, setItems] = useState([{ id: 1 }, { id: 2 }, { id: 3 }]);

  return (
    <DragDropContext onDragEnd={() => {}}>
      <List items={items} />
    </DragDropContext>
  );
};

export default App;

实战:打造个性化的拖拽效果

通过自定义样式和动画,你可以为拖拽元素添加更多的视觉效果,以提升用户体验。

示例代码
import React, { useState } from 'react';
import { DragDropContext, Droppable, Draggable, useDraggable, useDroppable } from 'react-beautiful-dnd';

const Item = ({ id, index }) => {
  const { isDragging, draggableProps, dragHandleProps } = useDraggable({
    id: id.toString(),
    index,
  });

  return (
    <Draggable draggableId={id.toString()} index={index}>
      {(provided) => (
        <div
          ref={provided.innerRef}
          {...draggableProps}
          {...dragHandleProps}
          style={{ padding: '10px', margin: '10px', backgroundColor: 'lightblue', transition: 'transform 0.3s ease', opacity: isDragging ? 0.5 : 1 }}
        >
          Item {id}
        </div>
      )}
    </DragDropContext>
  );
};

const List = ({ items }) => {
  const { isOver, droppableProps, innerRef } = useDroppable({
    type: 'item',
    droppableId: '1',
  });

  return (
    <Droppable droppableId="1" type="item">
      {(provided) => (
        <div ref={innerRef} {...droppableProps} style={{ padding: '10px', margin: '10px', backgroundColor: isOver ? 'lightgray' : 'lightblue' }}>
          {items.map((item, index) => (
            <Item key={item.id} id={item.id} index={index} />
          ))}
          {provided.placeholder}
        </div>
      )}
    </DragDropContext>
  );
};

const App = () => {
  const [items, setItems] = useState([{ id: 1 }, { id: 2 }, { id: 3 }]);

  return (
    <DragDropContext onDragEnd={() => {}}>
      <List items={items} />
    </DragDropContext>
  );
};

export default App;

高级用法

多列排序

多列排序是指在一个界面中,将多个列表进行拖拽排序。你可以通过多个 Droppable 组件来实现多列排序的功能。

示例代码
import React, { useState } from 'react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';

const Item = ({ id, index }) => (
  <Draggable draggableId={id.toString()} index={index}>
    {(provided) => (
      <div
        ref={provided.innerRef}
        {...provided.draggableProps}
        {...provided.dragHandleProps}
        style={{ padding: '10px', margin: '10px', backgroundColor: 'lightblue', transition: 'transform 0.3s ease' }}
      >
        Item {id}
      </div>
    )}
  </Draggable>
);

const Column = ({ id, items }) => (
  <Droppable droppableId={id.toString()} type="item">
    {(provided) => (
      <div ref={provided.innerRef} {...provided.droppableProps} style={{ padding: '10px', margin: '10px', backgroundColor: 'lightgray', width: '150px', border: '1px solid #ccc', borderRadius: '5px' }}>
        {items.map((item, index) => (
          <Item key={item.id} index={index} id={item.id} />
        ))}
        {provided.placeholder}
      </div>
    )}
  </DragDropContext>
);

const App = () => {
  const [items, setItems] = useState([
    { id: 1, columnId: '1' },
    { id: 2, columnId: '1' },
    { id: 3, columnId: '2' },
  ]);

  const [columns, setColumns] = useState([
    { id: '1', items: [{ id: 1 }, { id: 2 }] },
    { id: '2', items: [{ id: 3 }] },
  ]);

  const onDragEnd = (result) => {
    const { source, destination } = result;

    if (!destination) return;

    const dragId = items[source.index].id;
    const sourceColumn = columns.find((column) => column.id === source.droppableId);
    const destColumn = columns.find((column) => column.id === destination.droppableId);

    if (source.droppableId === destination.droppableId) {
      const targetId = destColumn.items[destination.index].id;
      const targetIndex = destColumn.items.findIndex((item) => item.id === targetId);
      columns[source.index].items.splice(source.index, 1);
      columns[source.index].items.splice(targetIndex, 0, { id: dragId });
    } else {
      const sourceColumnItems = sourceColumn.items;
      const destColumnItems = destColumn.items;

      columns[source.index].items.splice(source.index, 1);
      columns[destination.index].items.splice(destination.index, 0, { id: dragId });
    }

    setColumns([...columns]);
  };

  return (
    <DragDropContext onDragEnd={onDragEnd}>
      {columns.map((column, index) => (
        <Column key={column.id} id={column.id} items={column.items} />
      ))}
    </DragDropContext>
  );
};

export default App;

多种模式的拖拽支持

React-beautiful-dnd 支持多种拖拽模式,例如按住拖拽、拖放、选择拖放等。你可以根据具体需求选择合适的模式。

示例代码
import React, { useState } from 'react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';

const Item = ({ id, index }) => (
  <Draggable draggableId={id.toString()} index={index}>
    {(provided) => (
      <div
        ref={provided.innerRef}
        {...provided.draggableProps}
        {...provided.dragHandleProps}
        style={{ padding: '10px', margin: '10px', backgroundColor: 'lightblue', transition: 'transform 0.3s ease' }}
      >
        Item {id}
      </div>
    )}
  </Draggable>
);

const List = ({ items }) => (
  <Droppable droppableId="1" type="item">
    {(provided) => (
      <div ref={provided.innerRef} {...provided.droppableProps} style={{ padding: '10px', margin: '10px', backgroundColor: 'lightgray' }}>
        {items.map((item, index) => (
          <Item key={item.id} index={index} id={item.id} />
        ))}
        {provided.placeholder}
      </div>
    )}
  </DragDropContext>
);

const App = () => {
  const [items, setItems] = useState([{ id: 1 }, { id: 2 }, { id: 3 }]);

  return (
    <DragDropContext onDragEnd={() => {}}>
      <List items={items} />
    </DragDropContext>
  );
};

export default App;

实战:实现复杂的拖拽交互

通过上述高级用法,你可以实现更复杂和个性化的拖拽交互,例如多列排序、多种模式的支持等。

示例代码
import React, { useState } from 'react';
import { DragDropContext, Droppable, Draggable, useDraggable, useDroppable } from 'react-beautiful-dnd';

const Item = ({ id, index }) => {
  const { isDragging, draggableProps, dragHandleProps } = useDraggable({
    id: id.toString(),
    index,
  });

  return (
    <Draggable draggableId={id.toString()} index={index}>
      {(provided) => (
        <div
          ref={provided.innerRef}
          {...draggableProps}
          {...dragHandleProps}
          style={{ padding: '10px', margin: '10px', backgroundColor: isDragging ? 'lightgreen' : 'lightblue', transition: 'transform 0.3s ease' }}
        >
          Item {id}
        </div>
      )}
    </DragDropContext>
  );
};

const Column = ({ id, items }) => {
  const { isOver, droppableProps, innerRef } = useDroppable({
    type: 'item',
    droppableId: id.toString(),
  });

  return (
    <Droppable droppableId={id.toString()} type="item">
      {(provided) => (
        <div ref={innerRef} {...droppableProps} style={{ padding: '10px', margin: '10px', backgroundColor: isOver ? 'lightgray' : 'lightblue', width: '150px', border: '1px solid #ccc', borderRadius: '5px' }}>
          {items.map((item, index) => (
            <Item key={item.id} id={item.id} index={index} />
          ))}
          {provided.placeholder}
        </div>
      )}
    </DragDropContext>
  );
};

const App = () => {
  const [items, setItems] = useState([
    { id: 1, columnId: '1' },
    { id: 2, columnId: '1' },
    { id: 3, columnId: '2' },
  ]);

  const [columns, setColumns] = useState([
    { id: '1', items: [{ id: 1 }, { id: 2 }] },
    { id: '2', items: [{ id: 3 }] },
  ]);

  const onDragEnd = (result) => {
    const { source, destination } = result;

    if (!destination) return;

    const dragId = items[source.index].id;
    const sourceColumn = columns.find((column) => column.id === source.droppableId);
    const destColumn = columns.find((column) => column.id === destination.droppableId);

    if (source.droppableId === destination.droppableId) {
      const targetId = destColumn.items[destination.index].id;
      const targetIndex = destColumn.items.findIndex((item) => item.id === targetId);
      columns[source.index].items.splice(source.index, 1);
      columns[source.index].items.splice(targetIndex, 0, { id: dragId });
    } else {
      const sourceColumnItems = sourceColumn.items;
      const destColumnItems = destColumn.items;

      columns[source.index].items.splice(source.index, 1);
      columns[destination.index].items.splice(destination.index, 0, { id: dragId });
    }

    setColumns([...columns]);
  };

  return (
    <DragDropContext onDragEnd={onDragEnd}>
      {columns.map((column, index) => (
        <Column key={column.id} id={column.id} items={column.items} />
      ))}
    </DragDropContext>
  );
};

export default App;

常见问题与解决方案

React-beautiful-dnd常见问题

  • 拖拽元素无法拖动
  • 拖拽状态信息未更新
  • 拖拽元素在拖动过程中消失

解决方案与调试技巧

  • 确保 DraggableDroppable 组件的 idtype 配对正确。
  • 确保组件渲染顺序正确,Draggable 必须包含在 Droppable 内部。
  • 调试时可以打印拖拽状态信息,检查是否正确更新。

实战:解决拖拽过程中的常见问题

通过上述解决方案,你可以有效地解决拖拽过程中遇到的问题,确保拖拽功能正常工作。

示例代码
import React, { useState } from 'react';
import { DragDropContext, Droppable, Draggable, useDraggable, useDroppable } from 'react-beautiful-dnd';

const Item = ({ id, index }) => {
  const { isDragging, draggableProps, dragHandleProps } = useDraggable({
    id: id.toString(),
    index,
  });

  return (
    <Draggable draggableId={id.toString()} index={index}>
      {(provided) => (
        <div
          ref={provided.innerRef}
          {...draggableProps}
          {...dragHandleProps}
          style={{ padding: '10px', margin: '10px', backgroundColor: 'lightblue', transition: 'transform 0.3s ease', opacity: isDragging ? 0.5 : 1 }}
        >
          Item {id}
        </div>
      )}
    </DragDropContext>
  );
};

const Column = ({ id, items }) => {
  const { isOver, droppableProps, innerRef } = useDroppable({
    type: 'item',
    droppableId: id.toString(),
  });

  return (
    <Droppable droppableId={id.toString()} type="item">
      {(provided) => (
        <div ref={innerRef} {...droppableProps} style={{ padding: '10px', margin: '10px', backgroundColor: isOver ? 'lightgray' : 'lightblue', width: '150px', border: '1px solid #ccc', borderRadius: '5px' }}>
          {items.map((item, index) => (
            <Item key={item.id} id={item.id} index={index} />
          ))}
          {provided.placeholder}
        </div>
      )}
    </DragDropContext>
  );
};

const App = () => {
  const [items, setItems] = useState([
    { id: 1, columnId: '1' },
    { id: 2, columnId: '1' },
    { id: 3, columnId: '2' },
  ]);

  const [columns, setColumns] = useState([
    { id: '1', items: [{ id: 1 }, { id: 2 }] },
    { id: '2', items: [{ id: 3 }] },
  ]);

  const onDragEnd = (result) => {
    const { source, destination } = result;

    if (!destination) return;

    const dragId = items[source.index].id;
    const sourceColumn = columns.find((column) => column.id === source.droppableId);
    const destColumn = columns.find((column) => column.id === destination.droppableId);

    if (source.droppableId === destination.droppableId) {
      const targetId = destColumn.items[destination.index].id;
      const targetIndex = destColumn.items.findIndex((item) => item.id === targetId);
      columns[source.index].items.splice(source.index, 1);
      columns[source.index].items.splice(targetIndex, 0, { id: dragId });
    } else {
      const sourceColumnItems = sourceColumn.items;
      const destColumnItems = destColumn.items;

      columns[source.index].items.splice(source.index, 1);
      columns[destination.index].items.splice(destination.index, 0, { id: dragId });
    }

    setColumns([...columns]);
  };

  return (
    <DragDropContext onDragEnd={onDragEnd}>
      {columns.map((column, index) => (
        <Column key={column.id} id={column.id} items={column.items} />
      ))}
    </DragDropContext>
  );
};

export default App;
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消