Использование react-dnd с React Hooks

React.js

Я уже писал статью об использовании react-dnd, которая может быть многословной, но содержание более подробное, и перечислены многие API. В настоящее время React Hooks отсутствует, и react-dnd также был соответствующим образом обновлен, поэтому в этой статье используются React Hooks + TypeSscript для реорганизации использования react-dnd.

предисловие

Введение в текущую версию

  • react: 16.9.0
  • react-dom: 16.9.0
  • typescript: 3.5.3
  • react-dnd: 9.3.4
  • react-dnd-html5-backend: 9.3.4

используется для строительных лесовcreate-react-app, установлено глобальноcreate-react-appКоманда:npm i create-react-app -g

Строительство окружающей среды

  • Создать подмости:npx create-react-app react-dnd-hooks --typescript
  • Установитьreact-dnd:yarn add react-dnd
  • Установитьreact-dnd-html5-backend:yarn add react-dnd-html5-backend

Для удобства унификации идентификатора «перетаскиваемый компонент» заменяется на «перетаскиваемый компонент», а «целевой принимающий компонент» заменяется на «отбрасываемый компонент».


1. Оберните корневой узел с помощью DndProvider

хочу использоватьreact-dndДля операций перетаскивания вам необходимо использоватьDndProviderМетка оборачивает корневой узел и проходит вbackendпараметр:index.tsxдокумент

import React from 'react';
import ReactDOM from 'react-dom';
import { DndProvider } from 'react-dnd';
import HTMLBackend from 'react-dnd-html5-backend'
import './index.css';
import App from './App';

ReactDOM.render(
    <DndProvider backend={ HTMLBackend }>
        <App />
    </DndProvider>,
    document.getElementById('root'));

2. Позвольте элементам двигаться

Например, сейчас есть компонент Box, и мы хотим его перетащить.На данный момент давайте сначала объявим этот компонент.

Создайте компонент Box

Box.tsx

import React, { CSSProperties } from 'react';

const style: CSSProperties = {
    width: 200,
    height: 50,
    lineHeight: '50px',
    background: 'pink',
    margin: '30px auto'
}

const Box = () => {
    return (
        <div style={ style }>可拖拽组件 Box</div>
    )
}

export default Box;

В настоящее время мы используем мышь, чтобы удерживать компонент Box, и обнаруживаем, что нет возможности перетащить компонент Box.

Анимация компонента Box

использоватьreact-dndкоторый предоставилuseDrag, первое возвращаемое значение — это объект, возвращаемый методом collect (здесь он не используется, поэтому опущен), а второе возвращаемое значение — это ref, который присваивается элементу, который вы хотите перетащить для достижения перетаскивания компонента.Box.tsx

...
import { useDrag } from 'react-dnd';
...
const Box = () => {
    // 使用 useDrag
    const [, drager] = useDrag({
        item: { type: 'Box' }
    })
    return (
        // 将第二个参数赋值给 ref
        <div ref={ drager } style={ style }>可拖拽组件 Box</div>
    )
}

export default Box;

В это время мы можем использовать мышь для перетаскивания туда и обратноBoxКомпонент готов, но его можно только тащить, а датчик некуда братьBoxкомпонент, давайте посмотрим, как он работает.


3. Создайте компонент мусорной корзины, чтобы получить компонент перетаскивания.

использоватьreact-dndкоторый предоставилuseDrop, первое возвращаемое значение — это объект, возвращаемый методом collect, а второе возвращаемое значение — это ссылка, которая присваивается объекту, который вы хотите получить.dropКомпонент может воспринимать компонент перетаскивания.

DustbinКомпоненты:

import React, { CSSProperties } from 'react';
import { useDrop, DropTargetMonitor } from 'react-dnd';

const style: CSSProperties = {
    width: 400,
    height: 400,
    margin: '100px auto',
    lineHeight: '60px',
    border: '1px dashed black'
}

const Dustbin = () => {
    // 第一个参数是 collect 方法返回的对象,第二个参数是一个 ref 值,赋值给 drop 元素
    const [collectProps, droper] = useDrop({
        // accept 是一个标识,需要和对应的 drag 元素中 item 的 type 值一致,否则不能感应
        accept: 'Box',
        // collect 函数,返回的对象会成为 useDrop 的第一个参数,可以在组件中直接进行使用
        collect: (minoter: DropTargetMonitor) => ({
            isOver: minoter.isOver()
        })
    })
    const bg = collectProps.isOver ? 'deeppink' : 'white';
    const content = collectProps.isOver ? '快松开,放到碗里来' : '将 Box 组件拖动到这里'
    return (
        // 将 droper 赋值给对应元素的 ref
        <div ref={ droper } style={{ ...style, background: bg }}>{ content }</div>
    )
}

export default Dustbin;

Напоминание: не забудьте сослаться на компоненты Box и Dustbin на компонент App.tsx для использования.


В-четвертых, карта эффектов

Dustbin 效果图


5. Другие часто используемые API и меры предосторожности

  • dragЧасто используемые свойства компонентов:

    • item: это объект, должен бытьtypeАтрибуты

    • begin(mintor: DragSourceMonitor): Компонент начинает перетаскивание и должен вернуть объект, содержащийtypeсвойства, будет переопределятьitemОбъект, возвращаемый свойством, будет передан в первый параметр методов hover и drop компонента drop.

    • end(item, mintor: DragSourceMonitor): срабатывает, когда компонент перестает перетаскиваться,itemдаdropкомпонент вdropОбъект, возвращаемый при выполнении метода, эквивалентенmintor.getDropResult()значение

  • dropЧасто используемые свойства компонентов

    • accept: строка, должна соответствоватьdragкомпонентitemв атрибутеtypeзначение соответствует

    • hover(item, minoter: DropTargetMonitor):dragкомпонент вdropнад компонентомhoveвызывать

    • drop(item, minoter: DropTargetMonitor):dragПосле перетаскивания компонента поместите его наdropЗапускается при запуске компонента, возвращаемое значение будет передано в качестве параметра вdragкомпонентыendпервый параметр метода

  • Сделайте компонент перетаскиваемым и получайте перетаскиваемые элементы

import React, { useRef } from 'react';
import { useDrag, useDrop } from 'react-dnd'

const Card = () => {
    const ref = useRef<HTMLDivElement>(null);

    const [, drop] = useDrop({
        accept: 'Card',
    });

    const [, drag] = useDrag({
        item: { type: 'Card' }
    });

    // 使用 drag 和 drop 包装 ref
    drag(drop(ref));

    // 将变量 ref 传给元素的 ref 即可
    return (<div ref={ ref }>既可以被拖动也可以接收拖动组件</div>)
}
  • dragКомпонент хочет передать некоторые данные

    • Передайте его напрямую, используя свойство item:
      const [, drag] = useDrag({
          item: { type: 'Card', id: 1, name: 'card1', kind: 'Card }
      });
    
    • Используйте метод begin для передачи значений:

    Примечание. Возвращаемое значение метода begin переопределяет атрибут элемента, поэтому обязательно передайте атрибут типа.

      const [, drag] = useDrag({
          item: { type: 'Card' },
          begin(mintor: DragSourceMonitor) {
              return { type: 'Card', id: 1, name: 'card1', kind: 'Card }
          }
      });
    
    • позжеdropTargetВ принимающем компоненте его можно получить в первом параметре метода наведения или сброса, либо использоватьDropTargetMonitorизgetItem()приобретение функции.
  • хочу получитьdragкомпонент илиdropНекоторая информация о состоянии компонента

    • dragКомпоненты:
      // collect 函数返回的对象会赋给 useDrop 的第一个参数 collectProps,可以在组件中直接进行使用
      const [collectProps, drag] = useDrag({
          item: { type: 'Card', id: 1, name: 'card1', kind: 'Card },
          collect: (minoter: DropTargetMonitor) => ({
              isOver: minoter.isOver(),
          })
      });
    
    • dropКомпоненты:
      // collect 函数返回的对象会赋给 useDrop 的第一个参数 collectProps,可以在组件中直接进行使用
      const [collectProps, droper] = useDrop({
          accept: 'Box',
          collect: (minoter: DropTargetMonitor) => ({
              isOver: minoter.isOver(),
          })
      })
    

6. Более сложная демонстрация

Пример размещения с помощью перетаскивания и сортировки с помощью перетаскивания, заинтересованные друзья могут посмотреть.

визуализация

拖拽并排序预览

Демонстрационный адрес

react-dnd-hooks-demo

Добро пожаловать Звезда! Добро пожаловать в Форк!


7. Ссылки на ресурсы