基于react的拖拽组件库react-beautiful-dnd的简单使用

479 阅读3分钟

前言

自己通过HTML提供的拖拽API实现的拖拽功能,存在的问题:拖拽过程没有过度、输入框不能通过鼠标全选; 所以接入第三方插件库去实现。

react-beautiful-dnd.netlify.app/?path=/stor…

iwEdAqNwbmcDAQTRAfcF0QFcBrCuTzO6QFYswQVYHydS544AB9IqWhnwCAAJomltCgAL0gAA5bc.png_720x720q90.jpg

iwEcAqNwbmcDAQTRAgAF0QDNBrDSCGbZcJcY2gVYH16QuocAB9IqWhnwCAAJomltCgAL0gAAocM.png_720x720q90.jpg

组件

DragDropContext

github.com/atlassian/r…

必填props

  • onDragEnd: 返回拖拽的信息(result),如开始位置(result.source.index)、目标位置(result.destination.index)

Droppable

github.com/atlassian/r…

必填props

  • droppableId: 必填,保证唯一性(如果有多个同时需要实现拖拽的组件,详见

可选props

  • type: A TypeId(string) that can be used to simply accept only the specified class of <Draggable /><Draggable />s always inherit type from the <Droppable /> they are defined in. For example, if you use the type PERSON then it will only allow <Draggable />s of type PERSON to be dropped on itself. <Draggable />s of type TASK would not be able to be dropped on a <Droppable /> with type PERSON. If no type is provided, it will be set to 'DEFAULT'.
  • isDropDisabled: A flag to control whether or not dropping is currently allowed on the <Droppable />. You can use this to implement your own conditional dropping logic. It will default to false.
  • isCombineEnabled: A flag to control whether or not all the Draggables in the list will be able to be combined with. It will default to false.
  • direction: The direction in which items flow in this droppable. Options are vertical (default) and horizontal.
  • ignoreContainerClipping: When a <Droppable /> is inside a scrollable container its area is constrained so that you can only drop on the part of the <Droppable /> that you can see. Setting this prop opts out of this behavior, allowing you to drop anywhere on a <Droppable /> even if it's visually hidden by a scrollable parent. The default behavior is suitable for most cases so odds are you'll never need to use this prop, but it can be useful if you've got very long <Draggable />s inside a short scroll container. Keep in mind that it might cause some unexpected behavior if you have multiple <Droppable />s inside scroll containers on the same page.
  • modestandard (default) or virtual. Used to designate a list as a virtual list. See our virtual lists pattern
  • renderClone: used to render a clone (replacement) of the dragging <Draggable /> while a drag is occurring. See our reparenting guide for usage details. A clone must be used for virtual lists.  You can use a clone without using virtual lists
  • getContainerForClone: a function that returns the containing element (parent element) for a clone during a drag. See our reparenting guide.

标签必须包裹一个函数,并且返回react元素

<Droppable droppableId="droppable-1">
  {(provided, snapshot) => ({
    /*...*/
  })}
</Droppable>

该函数提供两个参数

provided说明

  • provided.innerRef: In order for the droppable to function correctly, you must bind the provided.innerRef to the highest possible DOM node in the ReactElement. We do this in order to avoid needing to use ReactDOM to look up your DOM node.

For more information on using innerRef see our using innerRef guide

  • provided.placeholder: This is used to create space in the <Droppable /> as needed during a drag. This space is needed when a user is dragging over a list that is not the home list. Please be sure to put the placeholder inside of the component for which you have provided the ref. We need to increase the size of the <Droppable /> itself.
  • provided.droppableProps (DroppableProps): This is an Object that contains properties that need to be applied to a Droppable element. It needs to be applied to the same element that you apply provided.innerRef to. It currently contains data attributes that we use for styling and lookups.

snapshot说明

image.png

通过这个参数暴露出的属性,可以根据需要设置一些样式交互,例如正在拖拽中的元素效果。 image.png

Draggable

github.com/atlassian/r…

栗子

import React, { Component } from "react";
import ReactDOM from "react-dom";
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";

// fake data generator
const getItems = count =>
  Array.from({ length: count }, (v, k) => k).map(k => ({
    id: `item-${k}`,
    content: `item ${k}`
  }));

// a little function to help us with reordering the result
const reorder = (list, startIndex, endIndex) => {
  const result = Array.from(list);
  const [removed] = result.splice(startIndex, 1);
  result.splice(endIndex, 0, removed);

  return result;
};

const grid = 8;

const getItemStyle = (isDragging, draggableStyle) => ({
  // some basic styles to make the items look a bit nicer
  userSelect: "none",
  padding: grid * 2,
  margin: `0 0 ${grid}px 0`,

  // change background colour if dragging
  background: isDragging ? "lightgreen" : "grey",

  // styles we need to apply on draggables
  ...draggableStyle
});

const getListStyle = isDraggingOver => ({
  background: isDraggingOver ? "lightblue" : "lightgrey",
  padding: grid,
  width: 250
});

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      items: getItems(10)
    };
    this.onDragEnd = this.onDragEnd.bind(this);
  }

  onDragEnd(result) {
    // dropped outside the list
    if (!result.destination) {
      return;
    }

    const items = reorder(
      this.state.items,
      result.source.index,
      result.destination.index
    );

    this.setState({
      items
    });
  }

  // Normally you would want to split things out into separate components.
  // But in this example everything is just done in one place for simplicity
  render() {
    return (
      <DragDropContext onDragEnd={this.onDragEnd}>
        <Droppable droppableId="droppable">
          {(provided, snapshot) => (
            <div
              {...provided.droppableProps}
              ref={provided.innerRef}
              style={getListStyle(snapshot.isDraggingOver)}
            >
              {this.state.items.map((item, index) => (
                <Draggable key={item.id} draggableId={item.id} index={index}>
                  {(provided, snapshot) => (
                    <div
                      ref={provided.innerRef}
                      {...provided.draggableProps}
                      {...provided.dragHandleProps}
                      style={getItemStyle(
                        snapshot.isDragging,
                        provided.draggableProps.style
                      )}
                    >
                      {item.content}
                      <input />
                    </div>
                  )}
                </Draggable>
              ))}
              {provided.placeholder}
            </div>
          )}
        </Droppable>
      </DragDropContext>
    );
  }
}

// Put the thing into the DOM!
ReactDOM.render(<App />, document.getElementById("root"));

效果图

a2273c68cc98cbec833e3d2c7c587f36ff25faebca78272d262cdfe9727c80f8QzpcVXNlcnNcNzdpcmNsb3VkXEFwcERhdGFcUm9hbWluZ1xEaW5nVGFsa1w3MTA1NDc5NTJfdjJcSW1hZ2VGaWxlc1wxNzAxMjQzNTU5MzcyXzkyQzNGMTE0LTI1RTEtNDFlNy04MzM3LTk5Q0UyQzk2MDE1NC5wbmc=.png

aa0b843c64155d95d020d21789dd99312c5356638b447c50fe1b7fd26047083bQzpcVXNlcnNcNzdpcmNsb3VkXEFwcERhdGFcUm9hbWluZ1xEaW5nVGFsa1w3MTA1NDc5NTJfdjJcSW1hZ2VGaWxlc1wxNzAxMjQzNTY1OTA2XzczREFEMDgxLTA4NDktNDhjMy1COEU2LTFBQTdBOTA4NjdCNS5wbmc=.png

报错

外层元素缺少ref

A setup problem was encountered.> Invariant failed: provided.innerRef has not been provided with a HTMLElement.

  • 控制台报错
  • 无法拖动 14855383b2ee24a5433271e130e6d0d8cd64ca65eec2135112a9be75cf877307QzpcVXNlcnNcNzdpcmNsb3VkXEFwcERhdGFcUm9hbWluZ1xEaW5nVGFsa1w3MTA1NDc5NTJfdjJcSW1hZ2VGaWxlc1wxNzAxMjQ5OTI4OTEzXzg5MDMxRUE2LTgyRTgtNGU0NC04Qjg0LTk2NzNGMURGRjhFNi5wbmc=.png

warning

Droppable内缺少provided.placeholder

image.png

官方说明

provided.placeholder: This is used to create space in the <Droppable /> as needed during a drag. This space is needed when a user is dragging over a list that is not the home list. Please be sure to put the placeholder inside of the component for which you have provided the ref. We need to increase the size of the <Droppable /> itself.

最后

拖拽控制台会报这个警告,目前还不知道对组件功能有什么影响

image.png