React DnDドキュメントの日本語化

# はじめに
このドキュメントは自分の備忘録的にこの分かりづらいReact DnDを読み解くために書いたものです。なので一部しか翻訳していませんが、ご了承ください。
Hooks APIを使っているのでHooks中心になっています。

## 概要
React DnD は今までの大抵のドラッグ＆ドロップライブラリとは違います。もしあなたがこれを使ったことないのであれば、威圧的なものになるでしょう。しかし、この設計のコアコンセプトを少しかじったことがあれば理解できるでしょう。残りのドキュメントを読む前にこれらのコンセプトについて一読するのをおすすめします。
コンセプトの一部は [Flux](http://facebook.github.io/flux/) や [Redux](https://github.com/reactjs/react-redux) のアーキテクチャに似ています。
これは偶然ではなく、React DnDの中でReduxを使っているからです。

## バックエンド
React DnD は [HTML5 drag and drop API](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Drag_and_drop)の上に作られています。 それはドラッグされているDOMノードをスクリーンショットし、”ドラッグプレビュー”として枠の外で使うので至って合理的です。これはカーソルの動きのような描画を一切しなくて済む点で便利です。このAPIはファイルドロップのイベントを操作する唯一の方法でもあります。
残念ながら、HTML5 drag and drop APIには良くない側面もあります。タッチスクリーンでは動かず、IEでは他のブラウザに比べてカスタマイズ性が下がります。

これが *HTML5 drag and drop サポートを使うことはReact DnDでは接続可能な一つの方法。*としている理由です。必ずしも使う必要はありません。タッチイベントやマウスイベントや他のイベントに沿った違う方法を取ることができます。 このような接続可能な方法はReact DnDではバックエンドと呼びます。  [HTML5 backend](http://react-dnd.github.io/react-dnd/docs/backends/html5) だけがライブラリとしてありますが、将来的には増えていく予定です。
バックエンドはReactのイベントシステムのように振る舞います。*ブラウザの違いを抽象化し、ネイティブなDOMイベントを実行します*
Reactのイベントシステムと似ているとはいえ、React DnDのバックエンドはReactやそのイベントシステムとの依存関係はありません。
すべてのバックエンドはDOMイベントをReact DnDが処理できる形のRedux actionに内部的に翻訳しています。

## Items と Types
Flux (または Redux)のように、React DnDはデータを扱い、viewを正しいソースとして使いません。何かをスクリーンをまたいでドラッグしたとき、我々はそれをコンポーネントやドラッグされているDOMノードとは呼日ません。そのかわりに、ドラッグされているあるtypeのitemと呼びます。
Itemとはなにか。itemはドラックされているものを示すプレーンなJavaScriptのオブジェクトです。
例えば、カンバンアプリにおいて、カードをドラッグしたときはitemは`{ cardId: 42 }`のようになり、チェスのゲームではピースをピックしたときitemは `{ fromCell: 'C5', piece: 'queen' }`のように見えます。*ドラッグされているデータをプレーンなオブジェクトとして表現することで、お互いのコンポーネントを切り離したり、知らなくしたりできるのです。*
typeとはなにか。typeはstring(または[symbol](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Symbol) ) であり、アプリケーションにおいて、itemのすべてのクラスをユニークに特定するためのものです。カンバンアプリにおいて、ドラッグできるカードは`card`というtypeで示され、これらのカードをまとめた、ドラッグできるリストは `list`type として示される。チェスでは一つ`piece`というtypeがあるだろう。

Typesはアプリケーションを成長させていく上でいろいろなものをドラッグできるようにさせていく際に便利になるだろう。 しかし、すべてのドロップターゲットが突然新しいitemに反応されてほしくはない。*Typesはどのドラッグ元とドロップターゲットが対応しているのかを指定することができる*おそらくアプリケーションではReduxのaction typesの列挙を持つように、type定数の列挙を持つだろう。

## Monitors
ドラッグしている状態であってもなくても、typeやitemがあってもなくても、ドラッグアンドドロップはStatefulと切り離すことはできません。
この状態はどこかで保持されています。

React DnDはmonitor と呼ばれる内部のstateストレージに薄いラッパーを通してこの状態をコンポーネントに提供します。

ドラッグアンドドロップの状態を追う必要のあるコンポーネントのどれでも、monitorから関連するStateを回収するコレクト関数を定義することができます。

チェスの駒がドラッグされているときにセルをハイライトしたいとします。
その時の`Cell`コンポーネントのコレクト関数は以下のようになります。

```js
function collect(monitor) {
  return {
    highlighted: monitor.canDrop(),
    hovered: monitor.isOver(),
  }
}
```

この例はReact DnDが`highlighted`と`hover`という更新された値をすべての`Cell`インスタンスに対してpropsとして渡すことを示しています。

## Connectors
 バックエンドがDOMのイベントをハンドリングしているが、そのコンポーネントはDOMを表現するためにReactを使っている場合、どのようにバックエンドはリッスンするDOMノードを知ることができるのか。

コネクタについてです。コネクタは`render`関数の中で*予め定義されたロール（ドラッグ元、ドラッグプレビュー、ドラッグターゲット）の一つをDOMノードにアサインすることができる。*
実際、コネクタは上で説明したコレクト関数の第一引数として渡されます。それではどのようにドロップターゲットを指定して使うことができるのかを見てみましょう。

```js
function collect(connect, monitor) {
  return {
    highlighted: monitor.canDrop(),
    hovered: monitor.isOver(),
    connectDropTarget: connect.dropTarget(),
  }
}
```

コンポーネントの`render`メソッドの中で、monitorから得られる両方のデータにアクセス可能。この関数はコネクターから得られます。

```jsx
render() {
  const { highlighted, hovered, connectDropTarget } = this.props;

  return connectDropTarget(
    <div className={classSet({
      'Cell': true,
      'Cell--highlighted': highlighted,
      'Cell--hovered': hovered
    })}>
      {this.props.children}
    </div>
  );
}
```

`connectDropTarget`はコンポーネントのルートのDOMノードが正しいドロップターゲットかをReactDnDに問い合わせます。そして、そのhoverやdropイベントはバックエンドによって処理されるでしょう。内部的にはあなたが指定したReact elementに [callback ref](https://facebook.github.io/react/docs/more-about-refs.html#the-ref-callback-attribute) を付与することで動作しています。コネクタによって返された関数はメモ化され`shouldComponentUpdate`による最適化で破壊されません。

## Drag Sources and Drop Targets
これまでのところ、itemsとtypesによって示されるDOMとデータ、そしてmonitorsやconnectorsによって得られる関数とともに動作するバックエンドについて触れてきた。React DnDがコンポーネントに何のpropsを注入していくかを説明しよう
しかし、どのようにしてこれらのpropsがコンポーネントに実際に注入されるのかを設定するのか。どのようにドラッグ＆ドロップのイベントの応答による副作用を実行するのか。React DnDの最重要抽象概念であるdrag元とdropターゲットについて見てみよう。*これらはtypesとitemsと副作用そしてコレクト関数をコンポーネントにまとめます*
ドラッグ可能なコンポーネントを作りたいときはいつでも、ドラッグ元の宣言があるコンポーネントでラップする必要があります。すべてのドラッグ元はあるtypeが登録されています。そしてコンポーネントのpropsからitemを提供するメソッドを実行する必要がある。ドラッグ＆ドロップイベントをハンドリングするいくつかの他のメソッドをオプショナルに指定することもできます。 ドラッグ元の宣言は与えられたコンポーネントのコレクト関数も指定することができます。
ドロップターゲットはドラッグ元にとても似ています。唯一違う点は一つのドロップターゲットが複数のitem typesを一度に設定できる点とitemを提供する代わりにitemのhoverやdropをハンドリングする点です。

## Higher-Order Components and Decorators
コンポーネントをどのようにラップするか。また、ラッピングが何を意味するのか。いままでHoCを触ったことがなければ、 [この記事](https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750)を読んでください。コンセプトと詳細について触れています。*HoCは単にReactのclassコンポーネントを受け、違うReactのclassコンポーネントを返す関数です。*ライブラリから提供されているラッピングコンポーネントはあなたのコンポーネントの`render`メソッドをレンダリングし、propsを流し込むだけでなく、便利な挙動を付け加えます。
React DnDの[DragSource](http://react-dnd.github.io/react-dnd/docs/api/drag-source) と[DropTarget](http://react-dnd.github.io/react-dnd/docs/api/drop-target) では,他のトップレベルのexportされた関数と同様にHoCとなっています。これらはドラッグ＆ドロップの魔法をあなたのコンポーネントに吹きかけます。
これらを使う注意点としては、２つの関数アプリケーションが必要です。例えば、 [DragSource](http://react-dnd.github.io/react-dnd/docs/api/drag-source) でどのようにあなたのコンポーネントをラップするかの例です。

```jsx
import { DragSource } from 'react-dnd'

class YourComponent {
  /* … */
}

export default DragSource(/* … */)(YourComponent)
```

 [DragSource](http://react-dnd.github.io/react-dnd/docs/api/drag-source) の最初の関数の呼び出しにあるパラメータの指定のあと、２つ目の関数が呼ばれ、最後にあなたのclassを通ることを注意してください。これは [currying](http://en.wikipedia.org/wiki/Currying) や [partial application](http://en.wikipedia.org/wiki/Partial_application) と呼ばれ、 この箱の外で動作するために [decorator syntax](http://github.com/wycats/javascript-decorators) が必要です。

```jsx
import { DragSource } from 'react-dnd'

@DragSource(/* … */)
export default class YourComponent {
  /* … */
}
```

このシンタックスを必ずしも使う必要はありません。使いたいのであれば
 [Babel](http://babeljs.io/)を使ってトランスパイルしましょう。`{ “stage”: 1 }`というのを[.babelrc file](https://babeljs.io/docs/usage/babelrc/) に記述します。
もしデコレータを使わないのであれば、一部のアプリケーションではまだ使いやすいでしょう。というのもいくつかの [DragSource](http://react-dnd.github.io/react-dnd/docs/api/drag-source) と [DropTarget](http://react-dnd.github.io/react-dnd/docs/api/drop-target)の宣言をJavaScriptで[_.flow](https://lodash.com/docs#flow)のような関数構成ヘルパーをつかって合体させることができます。デコレータを使えば、同様の効果を得るためにデコレータを積み重ねる事ができます。 

```jsx
import { DragSource, DropTarget } from 'react-dnd'
import flow from 'lodash/flow'

class YourComponent {
  render() {
    const { connectDragSource, connectDropTarget } = this.props
    return connectDragSource(
      connectDropTarget(),
      /* … */
    )
  }
}

export default flow(
  DragSource(/* … */),
  DropTarget(/* … */),
)(YourComponent)
```

## Putting It All Together
以下にすでに作成した`Card`コンポーネントをドラッグ元にラッピングする例を示します。

```jsx
import React from 'react'
import { DragSource } from 'react-dnd'

// Drag sources and drop targets only interact
// if they have the same string type.
// You want to keep types in a separate file with
// the rest of your app's constants.
const Types = {
  CARD: 'card',
}

/**
 * Specifies the drag source contract.
 * Only `beginDrag` function is required.
 */
const cardSource = {
  beginDrag(props) {
    // Return the data describing the dragged item
    const item = { id: props.id }
    return item
  },

  endDrag(props, monitor, component) {
    if (!monitor.didDrop()) {
      return
    }

    // When dropped on a compatible target, do something
    const item = monitor.getItem()
    const dropResult = monitor.getDropResult()
    CardActions.moveCardToList(item.id, dropResult.listId)
  },
}

/**
 * Specifies which props to inject into your component.
 */
function collect(connect, monitor) {
  return {
    // Call this function inside render()
    // to let React DnD handle the drag events:
    connectDragSource: connect.dragSource(),
    // You can ask the monitor about the current drag state:
    isDragging: monitor.isDragging(),
  }
}

function Card(props) {
  // Your component receives its own props as usual
  const { id } = props

  // These two props are injected by React DnD,
  // as defined by your `collect` function above:
  const { isDragging, connectDragSource } = props

  return connectDragSource(
    <div>
      I am a draggable card number {id}
      {isDragging && ' (and I am being dragged now)'}
    </div>,
  )
}

// Export the wrapped version
export default DragSource(Types.CARD, cardSource, collect)(Card)
```

これであなたは残りのReact DnDのドキュメントを読むのに十分な知識を身につけました！
 [tutorial](http://react-dnd.github.io/react-dnd/docs/tutorial) を始めましょう。

## Hooks API
### useDrag
```jsx
import { __EXPERIMENTAL_DND_HOOKS_THAT_MAY_CHANGE_AND_BREAK_MY_BUILD__ as dnd } from 'react-dnd'
const { useDrag } = dnd

function DraggableComponent(props) {
  const [collectedProps, ref] = useDrag({
    item: { id, type },
  })
  return <div ref={ref}>…</div>
}
```

**パラメータ**

- spec: 仕様オブジェクト。どう作るかの詳細は下を御覧ください。


**返り値の配列**

- Index 0: コレクト関数から返ってくるコレクトプロパティを含んだオブジェクト。コレクト関数が定義されていなければ空のオブジェクトが返る。

- Index 1: useするためのReactのref。specオブジェクトでrefが定義されていなければ、自動的に生成される。refはドラッグできる要素に付ける必要がある。


**Spec オブジェクトの中身**

- item: 必須。 ドラッグされるデータを示すプレーンなjsのオブジェクト。
これはドロップ先のコンポーネントが唯一ドラッグする元について知れる情報。なので、ドロップ先が知るべき最小の情報を載せる。
おそらく複雑な参照をここで渡してしまうと思う。ただ、ドラッグ元とドロップ先は関連づいているので、これを避けるのは難しい。
なので、`{ type, id }`みたいな形で返すのが望ましい.
`item.type`は必須。`string`かES6の`symbol`である必要がある。
ドロップターゲットに登録されているタイプと同じ`item`だけが反応します。
`items`と`types`についてはoverviewを読んでください。

- ref: オプショナル. refオブジェクトはドラッグできる要素を使うために付与します。セットされていなければ、１が作成され、返ります。

- preview: オプショナル. ドラッグ中のプレビューとして用いるHTML要素かアタッチされているref要素. これを作成するのに`useDragPreview`を使うのを検討してみてください。

- previewOptions: オプショナル. ドラッグのプレビューのオプションを示すためのプレーンなjsのオブジェクト

- options: オプショナル.プレーンなオブジェクト。
コンポーネントのpropsがスカラー（単なる値や関数でないもの）でないなら、`options`オブジェクトの中で独自の`arePropsEqual(props, otherProps)`を指定することでパフォーマンスを改善できます。特にパフォーマンスの問題がなければ気にしなくて良いです。

- begin(monitor): オプショナル. ドラッグが開始したときに発火する。
特に何も返す必要はないが、`spec`のデフォルトの`item`プロパティをオーバーライドしているときはオブジェクトが返ります。

- end(monitor): オプショナル.  ドラッグが終わったとき、止まったときに呼ばれる。`begin`関数の呼び出し時に毎回`end`関数が呼ばれることが保証されている。
互換性のあるドロップ先のコンポーネントかどうかをチェックするために`monitor.didDrop()`を使うこともできます。これが呼び出されると、ドロップ先の`drop()`関数からプレーンなオブジェクトが返り、`drop result`で指定されたドロップ先は`monitor.getDropResult()`として利用可能になる。 
このメソッドはFluxのactionを呼ぶのに最適な場所です。 
>備考: もしコンポーネントがドラッグ中にunmountされた場合、コンポーネントのパラメータはnullがセットされます。

* canDrag(monitor): オプショナル. ドラッグが許可されているかどうかを指定するために使います。常に許可したい場合は除外するだけで構いません。 
`props`を通した記述からドラッグを禁止したいときに便利です。
>備考: このメソッドの中では`monitor.canDrag()`は呼び出せません。

* isDragging(monitor): オプショナル. デフォルトではドラッグ操作を開始したドラッグ元がドラッグしているかを示します。カスタムした`isDragging`メソッドを定義することでこの挙動をオーバーライドすることができます。すると`props.id === monitor.getItem().id.` のような方たちで返ります。
オリジナルのコンポーネントがドラッグ中にunmountされてあとで違う親コンポーネントで復活させたい場合これを使いましょう
例えば、カンバンボードのようにリストをまたいでカードを動かすときに、カードにドラッグされている状態を保持させたい。技術的にはそのコンポーネントはunmountされ、違うリストに移動させるたびに毎回違うものがmountされる。
>備考: このメソッドの中では`monitor.isDragging()`は呼び出せません。

* collect: オプショナル. コレクト関数。コンポーネントに注入するpropsのプレーンなオブジェクトを返します`monitor`と`props`という２つのパラメータを受け取ります。 monitorとcollecting関数に関してはoverviewを読んでください。コレクト関数についての詳細は次の章で。

---

### useDragLayer
コンポーネントをdrag-layerとして使うためのhooks

```jsx
import { __EXPERIMENTAL_DND_HOOKS_THAT_MAY_CHANGE_AND_BREAK_MY_BUILD__ as dnd } from 'react-dnd'
const { useDragLayer } = dnd

function DragLayerComponent(props) {
  const collectedProps = useDragLayer(spec)
  return <div>…</div>
}
```

**パラメータ**

* *collect*: 必須. コレクト関数。コレクト関数。コンポーネントに注入するpropsのプレーンなオブジェクトを返します`monitor`と`props`という２つのパラメータを受け取ります。 monitorとcollecting関数に関してはoverviewを読んでください。コレクト関数についての詳細は次の章で。

**返り値**
コレクト関数からコレクトされたプロパティのオブジェクト

---

### useDragPreview
drag-layerとしてコンポーネントを使用するためのhooks

```jsx
import { __EXPERIMENTAL_DND_HOOKS_THAT_MAY_CHANGE_AND_BREAK_MY_BUILD__ as dnd } from 'react-dnd'
const { useDragPreview } = dnd

function DragLayerPreview(props) {
  const [DragPreview, preview] = useDragPreview(spec)
  const [collectedProps, ref] = useDrag({
    item: { id, type },
    preview,
  })

  return (
    <>
      <DragPreview />
      <div ref={ref}>…drag item…</div>
    </>
  )
}
```

**パラメータ**

- dragPreview: ドラッグのプレビューをレンダリングするrefForwardingコンポーネント
 
**返り値の配列**
 
- Index 0: renderメソッドでレンダリングするドラッグのプレビューのコンポーネント
 
- Index 1: ドラッグプレビューのrefオブジェクト。useDragでの仕様を通して得られる。

---

### useDrop
コンポーネントをドロップターゲットとして使うためのhooksです。

```jsx
import { __EXPERIMENTAL_DND_HOOKS_THAT_MAY_CHANGE_AND_BREAK_MY_BUILD__ as dnd } from 'react-dnd'
const { useDrop } = dnd

function myDropTarget(props) {
  const [collectedProps, ref] = useDrop({ accept })

  return <div ref={ref}>Drop Target</div>
}
```

**パラメータ**
 - spec:　仕様オブジェクト。どう作るかの詳細は下を御覧ください。

**返り値の配列**

- Index 0:コレクト関数から返ってくるコレクトプロパティを含んだオブジェクト。コレクト関数が定義されていなければ空のオブジェクトが返る。

- Index 1: useするためのReactのref。specオブジェクトでrefが定義されていなければ、自動的に生成される。refはドロップできるDOMのエリアに付ける必要がある。

**Specification Object Members**

- accept: 必須. コンポーネントの`props`から受け取ったstring、ES6のsymbolもしくはそれらの配列もしくはそれらを返す関数が入る。
このドロップターゲットはドラッグ元から提供されているitemのtypeまたはtypeにあっているものだけが反応する。
itemsとtypesについてはoverviewを読んでください。

- ref: オプショナル. ドラッグできる要素を使うために付与します。セットされていなければ、１が作成され、返ります。

- options: オプショナル. プレーンなオブジェクト。 コンポーネントのpropsがスカラー（単なる値や関数でないもの）でないなら、`options`オブジェクトの中で独自の`arePropsEqual(props, otherProps)`を指定することでパフォーマンスを改善できます。特にパフォーマンスの問題がなければ気にしなくて良いです。

- drop(item, monitor): オプショナル. 互換性のあるitemをターゲットにドロップしたときにundefinedであってもプレーンなオブジェクトであっても呼ばれる。オブジェクトを返した際にはそれはdrop resultになり、drag元の`endDrag`メソッドで`monitor.getDropResult()`を通して利用可能になります。これはドロップを受け取るターゲットによって違うアクションをしたいときに便利です。  ネストされたドロップターゲットがある場合、`monitor.didDrop()`と`monitor.getDropResult()`をチェックすることでネストされたターゲットがすでに`drop`をハンドリングしているかを確認することができます。この両方のメソッドとドラッグ元の`sendDrag`メソッドはFluxのactionを発火するのに適した場所です。 このメソッドは`canDrop()`が定義されいて、`false`を返す場合は呼び出されません。

- hover(item, monitor): オプショナル. コンポーネントの上をitemがhoverしたときに呼ばれる。`monitor.isOver({ shallow: true })`をチェックすることでhoverが今のターゲットの上またはネストされたターゲットの上にあるかを確かめることができます。`drop()`とは違い、このメソッドは`canDrop()`が定義されていて、`false`を返すとしても呼ばれます。 `monitor.canDrop()`をチェックすることでそのケースかどうかを確かめることができます。

- canDrop(item, monitor): オプショナル. ドロップターゲットがitemを許可しているかを特定するために使います。常に許可したい場合は除外するだけで構いません。 
`props`または`monitor.getItem()`を通した記述からドロップを禁止したいときに便利です。
> 備考: このメソッドの中では`monitor.canDrop()`は呼び出せません。 

- collect: 必須. コレクト関数。コレクト関数。コンポーネントに注入するpropsのプレーンなオブジェクトを返します`monitor`と`props`という２つのパラメータを受け取ります。 monitorとcollecting関数に関してはoverviewを読んでください。コレクト関数についての詳細は次の章で。



## 最後に
このドキュメントは執筆途中なので、リクエストや場違い指摘などあればコメント等お願いいたします！