ReactDnDについて

Reactでdrag&dropコンポーネントを実装するのにおそらく一番有名(Redux作った人が作った)かつドキュメントが豊富なパッケージです。ドキュメントの情報量が結構多く自由度が高くて混乱しやすいので軽く使ってみたい人向けに核となるところだけ解説します。Danさん本当好き。

API

各コンポーネントをdrggable&droppable化するためのAPIがES7のdecoratorとして提供されています。
babel6使っている人はdecoratorがまだ公式では対応していないみたいなので注意してください(babel5なら大丈夫です)。公式じゃなければbabel6用のプラグイン作っている人がいたと思うので探してみてください。

DragSource

ドラッグされるコンポーネントについての設定を行える。

import React from "react";
import { DragSource } from "react-dnd";
@DragSouce(type, spec, collect)
export default class DragComponent extends React.Component {
// something
}

type: dorpされる側はここで設定したtypeを見てdropを受け入れるか否かを決める、SymbolかString。

spec: drag開始時の処理、drag終了時(dropされた時)の処理とかを書いたObject。例が後半にあります。

collect: DragComponent内で使う関数を取り出す関数でobjectを返す必要があります。connectとmonitorが引数として渡されます。ざっくり言うとconnectはDOMについて、monitorはdrag&dropの状態についてのObjectです、結構色々取れるので公式ドキュメント見てみてください。

DropTarget

ドロップされるコンポーネントについての設定を行える。

import React from "react";
import { DropTarget } from "react-dnd";
@DropTarget(types, spec, collect)
export default class DropComponent extends React.Component {
// something
}

types: dropを受け入れるtypeを設定する、SymbolかStringかArray。

spec: dropを受け入れた時の処理やhoverされている時の処理を書いたObject。例が後半にあります。

collect: DragSourceのcollectと同じ

DragDropContext

上記のコンポーネント達をこいつでラップすることで初めてdrag&dropができるようになる。Backendが必要。

import React from "react";
import HTML5Backend from "react-dnd-html5-backend";
import { DragDropContext } from "react-dnd";
@DragDropContext(HTML5Backend)
export default class DnDComponent extends React.Component {
// something
render() {
return (
// DragComponent & DropComponent
)
}
}

例ではHTML5Backendを使っているのでタッチには対応していません。タッチ対応のBackendもあるのでタッチ対応させたい人は探してみてください。

実装例

drag&dropするとメッセージを表示するコンポーネントを作ってみます。

DnDItem Componentを作る

実際にdrag&dropされるコンポーネントを作ります。
DragSourceとDropTargetは同時に使うことでdragもdropもできるコンポーネントを作ることが可能です。

drag&dropされたときのactionは親からpropsとして受け取ります。
またtypeも同様に親からpropsとして受け取っています。

DragSourceとDropTarget間は基本的にmonitorを通して値のやり取りをします。

import React from "react";
import { DragSource, DropTarget } from "react-dnd";
const dragSpec = {
// dragが始まったときの処理
beginDrag(props) {
// dragされ始めたら自分のidを返す
const { id } = props;
return { id };
},
// dragが終わったときの処置
endDrag(props, monitor) {
// beginDragで返されたidを取ってくる
const source = monitor.getItem();
// dropSpecのdropで返されたidを取ってくる
const target = monitor.getDropResult();
// dropActionを発火させる
if (target) props.dropAction(source.id, target.id);
}
}
const dropSpec = {
// dropされたときの処理
drop(props, monitor, component) {
// dropされたら自分のidを返す
const { id } = props;
return { id };
}
}
// DropTargetとDragSourceを使っているのでdragもdropもできる
@DropTarget(props => props.type, dropSpec, connect => ({ connectDropTarget: connect.dropTarget() }))
@DragSource(props => props.type, dragSpec, connect => ({ connectDragSource: connect.dragSource() }))
export default class DnDItem extends React.Component {
static propTypes = {
connectDragSource: React.PropTypes.func.isRequired,
connectDropTarget: React.PropTypes.func.isRequired,
dropAction: React.PropTypes.func.isRequired,
id: React.PropTypes.string.isRequired,
name: React.PropTypes.string.isRequired
};
render() {
const {
connectDragSource,
connectDropTarget,
name
} = this.props;
return connectDragSource(connectDropTarget(
<li>
<h3>{name}</h3>
</li>
));
}
}

DnDField Componentを作る

DnDItem Componentをラップする親玉を作ります、stateはこのコンポーネントで管理して更新用のactionをDnDItemに渡します。

今回はidをkeyとして渡しています、keyはReactDnD内部でのDOMの参照に使われるのでValueObjectである必要があります。あまりにも適当なものを渡すと壊れるので気をつけてください(React始めたばっかりの時Math.random()とか渡してて死にそうになった)。

import React from "react";
import HTML5Backend from "react-dnd-html5-backend";
import { DragDropContext } from "react-dnd";
import DnDItem from "./DnDItem";
// DragDropContextでラップする
@DragDropContext(HTML5Backend)
export default class DnDField extends React.Component {
constructor(props) {
super(props);
this.state = {
list: [
{id: "1", name: "foo"},
{id: "2", name: "bar"},
{id: "3", name: "bad"},
{id: "4", name: "qux"}
],
message: ""
};
}
dropAction(sourceId, targetId) {
const { list } = this.state;
// message更新 Redux使うならここでaction, それぞれのidが渡ってくる
const sourceName = list.find(item => item.id === sourceId).name;
const targetName = list.find(item => item.id === targetId).name;
this.setState({message: `${sourceName} dropped on ${targetName}`});
}
render()
const { list, message } = this.state;
const itemType = Symbol("item");
return (
<div>
<h1>{message}</h1>
<ol>
{list.map(item =>
<DnDItem id={item.id} name={item.name} type={itemType} dropAction={::this.dropAction} key={item.id}/>
)}
</ol>
</div>
)
}
}

まとめ

これでdragされたComponentのidとdropされたComponentのidが取得できる実装が出来ました。idが取得できればあとはどうにでもできるので実際の開発に生かすことができればと思います。

今回紹介したのはReactDnDのほんの一部で他にもたくさんのオプションがあるので是非ドキュメントを読んでカスタムしてみてください。

なぜHexoなのか

  • Node.jsでやれる
  • めっちゃ簡単に静的サイトを構築できる
  • テーマが多くて完成度が高い、さらにカスタムできる
  • Markdownで記事が書けるので嬉しいよ!

Hexoを使ってブログを公開するまでの手順

Hexoをインストールする

まずはnpmでHexoをインストールします。

npm install -g hexo

次にブログの雛形を作成して依存パッケージをインストールします。

hexo init <your-blog-name>
cd <your-blog-name>
npm install

ここまでくれば実際にページを見ることが可能です、hexo serverと打った後localhost:4000にアクセスしてみてください。デフォルトのテーマなので好みはあると思いますが、テーマは後で変えられるので安心してください!

Github Pagesでブログを公開する

ブログをGitHub Pagesを利用して公開してみようと思います、まずは<user-name>.github.ioという名前のレポジトリを作成します。
デプロイはGitで行いたいと思うのでプラグインをインストールします。

npm install hexo-deployer-git --save

次にレポジトリの登録を行います、先ほど作成した雛形の中にある_config.ymlを下記のように編集してください。

# Deployment
deploy:
type: git
repo: git@github.com:<user-name>/<user-name>.github.io.git
branch: master

これでブログを公開する準備は完了です、デプロイしてみましょう。

hexo deploy -g

無事にデプロイできたらhttp://<user-name>.github.ioにアクセスしてみてください、ブログが表示されれば成功です!

記事の追加をする

記事の追加は下記のコマンドを打つことで可能です、タイトルがそのままファイル名になるので日本語などは避けたほうが良いです。

hexo new "page-title"

そうするとsource/_postsの中にpage-title.mdができていると思います、Markdownでゴリゴリ中身を書きましょう。hexo serverでサーバーを動かしながら書くとブラウザをリロードするたびに更新されていくのでプレビュー代わりに使えます。
記事を書き終えたら公開して、Github Pagesにアクセスして実際に見てみてください。

hexo deploy -g

テーマをインストールする

まずはGithubなどで導入したいテーマを見つけてきてください、多分hexo themeとかで検索するとたくさん出てくると思います。細かい部分は自分で変更できるのでだいたい雰囲気で選んで良いと思います。良い感じのテーマが見つかったらクローンしてきます。

cd themes
git clone git@github.com:<theme-auther>/<theme-name>.git

次に先ほどの_config.ymlを書き換えます。

# Extensions
theme: <theme-name>

これでテーマが適応されました、hexo serverして実際に見てみてください。
テーマの細かい設定はそれぞれのテーマのthemes/<theme-name>/_config.ymlを変更することで可能です。
ある程度気にいる形になったらデプロイして公開しましょう!

テーマやスタイルシートを変更した後にデプロイしてもデザインが変更されないことがあると思いますが、これは下のコマンドで解決することができます。

hexo clean

この後に再びデプロイしてみてください、テーマやデザインシートの変更が反映されていると思います!

まとめ

Hexoを使ってブログを公開する方法を書きました。
Hexoは静的サイトの構築が本当に簡単にできる素晴らしいツールだと思います、テーマも非常に完成度が高いものが多いので一瞬にしてそれなりにちゃんとしたブログが作れてしまうのは本当に驚きです。
ただHexoを使っていてひとつ辛いのは中国圏のユーザーが多いので英語ではなく中国語のドキュメント、ディスカッションが多いという点です、英語ならそれなりに読めますが中国語は辛い…