Open6

react-dndについて

shimaponshimapon

DndProvider

最初にセットアップする必要があるのが,DndProvider.
これはアプリのトップ付近でマウントされている必要がある.

import React from 'react'
import { DndProvider } from 'react-dnd'
import { HTML5Backend } from 'react-dnd-html5-backend'

function Board() {
  /* ... */
  return <DndProvider backend={HTML5Backend}>...</DndProvider>
}
shimaponshimapon

useDrag

ドラッグ可能にする(Draggable)

変数を宣言する

  • propsObject -> isDragging
  • ref関数 DOM要素を react-dnd にアタッチするために使われる.-> drag

useDragの引数(item, collect)

  • item.typeproperty(必須) ドラッグされるアイテムの種類を指定する
  • collect関数 ドラッグ&ドロップシステムの状態をコンポーネントで使用可能にする
  const [{isDragging}, drag] = useDrag({
    item: { type: 'knight' },
    collect: monitor => ({
      isDragging: !!monitor.isDragging(),
    }),
  })

全体

import React from 'react'
import { useDrag } from 'react-dnd'

function Knight() {
  const [{isDragging}, drag] = useDrag({
    item: { type: 'knight' },
    collect: monitor => ({
      isDragging: !!monitor.isDragging(),
    }),
  })

  return (
    <div
      ref={drag}
      style={{
        opacity: isDragging ? 0.5 : 1,
        fontSize: 25,
        fontWeight: 'bold',
        cursor: 'move',
      }}
    ></div>,
  )
}

export default Knight
shimaponshimapon

useDrop

ドロップターゲットを設定(Droppable)

同様にuseDropを使って変数を定義

const [{ isOver }, drop] = useDrop({
  accept: 'knight',
  drop: () => moveKnight(x, y),
  collect: (monitor) => ({
    isOver: !!monitor.isOver()
  })
})

全体

import React from 'react'
import Square from './Square'
import { moveKnight } from './Game'
import { useDrop } from 'react-dnd'

function BoardSquare({ x, y, children }) {
  const black = (x + y) % 2 === 1
  const [{ isOver }, drop] = useDrop({
    accept: 'knight',
    drop: () => moveKnight(x, y),
    collect: monitor => ({
      isOver: !!monitor.isOver(),
    }),
  })

  return (
    <div
      ref={drop}
      style={{
        position: 'relative',
        width: '100%',
        height: '100%',
      }}
    >
      <Square black={black}>{children}</Square>
      {isOver && (
        <div
          style={{
            position: 'absolute',
            top: 0,
            left: 0,
            height: '100%',
            width: '100%',
            zIndex: 1,
            opacity: 0.5,
            backgroundColor: 'yellow',
          }}
        />
      )}
    </div>,
  )
}

export default BoardSquare
shimaponshimapon

簡単に自分でやってみた

React.FC + TS

親コンポーネント:Board
子(ドラッグ): DragSource
子(ドロップ):DropTarget

(react, typescript未熟なので,良くない記述をしているかも)

Board.tsx
import React, { useState } from "react";
import DragSource from "./DragSource";
import DropTarget from "./DropTarget";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";

const Board: React.FC = () => {
  const [inObject, SetinObject] = useState(false);

  const handleDrop = () => {
    SetinObject(true);
  };

  return (
    <DndProvider backend={HTML5Backend}>
      {inObject ? null : <DragSource />}
      ↓ここにドロップできます
      <DropTarget onDrop={handleDrop}>
        {inObject ? <DragSource /> : null}
      </DropTarget>
    </DndProvider>
  );
};

export default Board;
DragSource.tsx
import React from "react";
import { useDrag } from "react-dnd";

const DragSource: React.FC = () => {
  const [{ isDragging }, drag] = useDrag({
    item: { type: "dragobject" },
    collect: (monitor) => ({
      isDragging: !!monitor.isDragging(),
    }),
  });

  return (
    <div
      style={{
        backgroundColor: "black",
        color: "white",
        width: "100px",
        height: "100px",
        margin: "1em",
        opacity: isDragging ? 0.5 : 1,
        cursor: "move",
      }}
      ref={drag}
    >
      ドラッグできます
    </div>
  );
};

export default DragSource;
DropTarget.tsx

import React from "react";
import { useDrop } from "react-dnd";

interface Props {
  onDrop: any;
}

const DropTarget: React.FC<Props> = ({ onDrop, children }) => {
  const [isOver, drop] = useDrop({
    accept: "dragobject",
    drop: () => onDrop(),
    collect: (monitor) => ({
      isOver: !!monitor.isOver(),
    }),
  });

  return (
    <div
      ref={drop}
      style={{
        width: "150px",
        height: "150px",
        border: "4px solid",
        margin: "1em",
      }}
    >
      {children}
    </div>
  );
};

export default DropTarget;