在本文中,我们将使用其中一个拖放库进行反应。很少有像react-dnd、dnd-kit和react-beautiful-dnd这样的反应好的拖放库。

我们今天将研究 dnd-kit 使用这个库的原因是它支持很多用例、钩子的可用性、轻量级等。

首先,让我们使用 create-react-app 创建一个反应应用程序并使用它安装必要的库

npx create-react-app react-dndkit-eg

npm install --save @dnd-kit/core @dnd-kit/sortable

@dntkit/core 和 @dndkit/sortable 这两个库是我们支持基本拖放功能所需要的,库还提供对其他库(如@dnd-kit/utilities@dnd-kit/modifiers@dnd-kit/accessibility)的各种其他功能的支持,可以阅读有关每个库的更多详细信息在他们的网站上。

首先,我们将创建一个组件,该组件将包装我们的可拖动/可排序组件,简单来说,dnd 的整个想法就是拥有一个容器,您的项目可以在其中拖入或移动,因此有关该组件的代码看起来会有点像这样sortable-component-complete-code

...
# e.g code snippet
export function SortableItem(props) {
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition,
  } = useSortable({id: props.id}); 

  const style = {
    transform: CSS.Transform.toString(transform),
    transition,
  };

  if(props.handle) {
    return (<div ref={setNodeRef} style={style} {...attributes} className={'pos-relative'}>
      {
        props.handle ? 
        <span className='dragHandleClass' {...listeners}>
          # some svg/img/htmlelement
        </span>
        : null
      }
      {props.children}
    </div>)
  }

  return (
    <div ref={setNodeRef} style={style} {...attributes}   {...listeners} className={'pos-relative'}>
      {props.children}
    </div>
  );
}
...

进入全屏模式 退出全屏模式

上面的代码是对需要拖拽/排序的组件的封装,后面会讲到handle prop,将idprop传递给useSortable hook,这样每个item都可以被唯一标识。

现在让我们创建一个组件,该组件将包含多个可以排序或拖动的项目,以创建一个我们需要 DndContext 和 SortableContext 的容器,以便可以移动/排序网格和行元素。

DndContext 需要一些道具,其中一些是传感器,collisionDetection 等,这些还包括像 handleDragStart 和 handleDragEnd 这样的函数,这些是可以在整个拖放交互之前和之后使用的函数。

Similarly SortableContext takes few props , here we will have to pass items which should be an array on uniqueIds & this should be same as that we have passed to sortableItem above.

上下文的代码应该类似于这个sortable-context-complete-code

...
# e.g sortable context/container

<DndContext 
          id={'grid-dnd-basic'}
          onDragEnd={handleDragEnd}
          sensors={sensors}
          collisionDetection={closestCenter}
        >
            <SortableContext 
              id={'grid-sort-contextbasic'}
              items={items.map((i) => i?.id)}
            >
              {items.map(value => {

                return (
                  <SortableItem handle={true} key={value?.id} id={value?.id} >
# sortableItem Content
                  </SortableItem>
                )
              })}
            </SortableContext>
          </DndContext>
...

进入全屏模式 退出全屏模式

至此,我们完成了有关拖放的组件设置,现在我们必须将处理程序添加到诸如 handleDragStart/handleDragEnd 之类的函数中,这些代码与 dnd-kit 文档提供的代码几乎相似,唯一的变化是 items 属性传递给处理函数


  function handleDragEnd(event) {
    const {active, over} = event;

    if (active.id !== over.id) {
      setItems((items) => {
        const oldIndex = (items.map(i => i.id)).indexOf(active.id);
        const newIndex = (items.map(i => i.id)).indexOf(over.id);

        return arrayMove(items, oldIndex, newIndex);
      });
    }
  }

进入全屏模式 退出全屏模式

在上面的代码中,您可以看到我们使用 map 函数仅传递 indexOf 函数的 id,因为所有内容都映射到传递给可排序项目和可排序上下文的唯一 Id。

所以我们几乎已经准备好使用 dndContext 和 sortableContext 进行排序的 dnd-kit 实现了。

Now lets visit the handle prop that we have used earlier in our sortableItem, so we can see useSortable provides a listeners now if we want to drag the item using some handler and not the actual item then we can use handle prop to apply listener to the drag-handler directly, in this way we will be able to drag via some handler and not he actual item

# e.g handle implementation
...
  if(props.handle) {
    return (<div ref={setNodeRef} style={style} {...attributes} className={'pos-relative'}>
      {
        props.handle ? 
        <span className='dragHandleClass' {...listeners}>
          # svg/image/html-element
        </span>
        : null
      }
      {props.children}
    </div>)
  }
  ...

进入全屏模式 退出全屏模式

现在让我们来谈谈整个传感器例如,我们将在文档中使用与此类似的基本传感器实现

  const sensors = useSensors(
    useSensor(PointerSensor),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates,
    })
  );

进入全屏模式 退出全屏模式

现在这些使用传感器将第二个参数作为对象,该对象也具有属性activationConstraint,现在这可以用于仅在一些像素移动代码后激活传感器。

  const sensors = useSensors(
    useSensor(PointerSensor, { activationConstraint: { distance: 5 } }),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates,
    })
  );

进入全屏模式 退出全屏模式

The scenarios where this can be used is when you have an click listener to you sortableItem & you don't use drag-handle, so that we can uniquely identify click & drag events, without this constraint event click will be treated as drag events & our drag-handler functions like onDragEnd & onDragStart will be triggered.

本文主要涵盖与dndContext和sortable预设相关的主题,更多关于相同的内容可以在 dnd-kit 网站上阅读。

与文章相关的代码存在于GithubLink&gh-pages

Logo

React社区为您提供最前沿的新闻资讯和知识内容

更多推荐