jQuery
jquery.ui
reactjs

Reactでjquery-uiのsortableを使わざる得ない場合の対処法

More than 1 year has passed since last update.

Reactでjquery-uiのsortableを使わざる得ない場合の対処法

作成経緯

フィクションです。

  • マネージャー「えーきみーReactできるらしいねー、○○○さんの管理画面の修正で使ってみる?」
  • 私「は、はい(朝礼でドヤ顔しておいてよかったぜ」
  • マネージャー「管理画面の入力と表示を同期させたいみたいだから、デザイナーさんと組んでやってみて」
  • 私「わかりました(新しいこと試せるやったぜ」
  • デザイナー「入力フォームはReactでリスト表示はjQueryUIのsortableを使い並び替えをできるようにしたまえ、いいね」
  • 私「アッハイ」

テストで叩いたソースコード

デモサイト

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8"/>
    <meta content='text/html; charset=UTF-8' http-equiv='Content-Type'/>
    <title>React jquery-ui sortable</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/4.0.0/normalize.min.css"/>
    <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.css"/>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.js"></script>
    <script type="text/javascript" src="https://fb.me/JSXTransformer-0.13.2.js"></script>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
    <script type="text/javascript" src='https://cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.3/jquery.ui.touch-punch.min.js'></script>
    <style>
        li {
            cursor: pointer;
        }
    </style>
</head>
<body>

<div id="content"></div>

<script type="text/jsx">
    // 並び替え項目コンポーネント
    var Item = React.createClass({
        render: function () {
            return <li id={this.props.item.id} className="">{this.props.item.name}</li>;
        }
    });

    // 並び替え一覧コンポーネント
    var List = React.createClass({
        // sortable実行
        onSortable: function () {
            // スコープ変わってもthisにアクセスできるようにする
            var self = this;
            // このコンポーネントを並び替え可能にする
            var $sortable = $(ReactDOM.findDOMNode(this));
            // 並び替え可能実行
            $sortable.sortable({
                stop: function () {
                    // 並び替えた値の置き場
                    var reOrderItems = [];
                    // ならび順のDOMID取得しその順序で値を並び替える
                    Array.prototype.forEach.call($sortable.sortable("toArray"), function (id) {
                        reOrderItems.push(
                            self.state.items.filter(function (ele) {
                                return ele.id == id;
                            })[0]
                        )
                    });
                    // stateとの整合性を保つため、DOMの並び順はリセット
                    $sortable.sortable("cancel");
                    // stateを書き換えて値とDOMの並び順の整合性を保つ
                    self.setState({items: reOrderItems});
                }
            });
        },
        // 更新時にすでにこのコンポーネントが並び替え可能になっているとエラー出るので、
        // sortableの初期化して、再度sortableする
        onSortableDestroy: function () {
            $(ReactDOM.findDOMNode(this)).sortable("destroy");
        },
        getInitialState: function () {
            return {
                items: [
                    {id: 1, name: "Item1"},
                    {id: 2, name: "Item2"},
                    {id: 3, name: "Item3"},
                    {id: 4, name: "Item4"}
                ]
            };
        },
        // Componentが更新する前に呼ばれます。初回時には呼ばれません。
        componentWillUpdate: function () {
            this.onSortableDestroy()
        },
        // ComponentがDOMから削除される時に呼ばれます。
        componentWillUnmount: function () {
            this.onSortableDestroy()
        },
        //HTML描画 render() 後の処理
        componentDidMount: function () {
            this.onSortable();
        },
        // コンポーネントが更新されたときに、DOMを操作する機会としてこれを使用してください。
        componentDidUpdate: function () {
            this.onSortable();
        },
        render: function () {
            return <ul className="">
                {Array.prototype.map.call(this.state.items, function (e) {
                    return <Item key={e.id} item={e}/>;
                })}
            </ul>;
        }
    });

    ReactDOM.render(
        <List/>,
        document.getElementById('content')
    );
</script>
</body>
</html>

結論

どぼぢでごんなごどずるのぉぉぉ!?
ReactのコンポーネントをJQueryでいじったらわけわからなくなるでしょぉぉぉ!!!

なので、jQueyUIのsortableで動かしてはいるものの、
並んだ順序だけ取得し、速攻でキャンセルしてstate書き換えて
jQueyUIで並び替えたように見せかけるということをしました。

フィクションですが、このような苦しみを味わう人がいるのではないかという思いを記事にしました。

参考元
React.js + jQuery UI Sortable
Here's an example of React + jQuery UI sortable.