🔀

React(dnd-kit) & Railsで画像の並び替え

2023/09/07に公開

何がしたかったのか、したくなかったのか

  • 画像の登録や並び替えや削除をしたい
  • でも登録や削除や並び替えの際に毎回serverにリクエストを送りたくない
  • 更新ボタンを押す等、一回のリクエストで済ませたい

技術調査

まず最初に見つけたのがこのライブラリ

https://egghead.io/courses/beautiful-and-accessible-drag-and-drop-with-react-beautiful-dnd

上記のライブラリで実装したサンプルがあったので挙動を確認

1列だけだと正常に動作するのですが、2列になると動作がおかしくなる。。。

ということが判明。

https://codesandbox.io/embed/horizontal-list-forked-2tobkj?fontsize=14&hidenavigation=1&theme=dark

将来的にwrapはsupportされない、つまり2列目がおかしくなる挙動はライブラリ側で直してくれないことが判明。

https://github.com/atlassian/react-beautiful-dnd/issues/316

以下のライブラリが良いという情報を入手、
だが将来的に廃止になることが判明
https://github.com/clauderic/react-sortable-hoc

上記のreact-sortable-hocと同じ作者が精力的に現在作ってるライブラリを発見。
https://github.com/clauderic/dnd-kit

めっちゃいい感じだったのライブラリだったので
dnd-kitを使うことに決定😊

https://dndkit.com/


概観(client - json - api)

client

id?でidがない場合も許容しているのは、clientが新しく追加したばかりの画像にはidがないためです(dbで採番しているため)

type Image {
  id?: number
  image: string
  position: number
  itemKey: string
}

json

どんなデータをやり取りするのか

{
"item": {
        "name": "商品名xxx"
        "images": {
                "id": 12489,
                "image": "blob data xxxx....",
                "position": "1",
                },
                {
                "image": "blob data xxxx....",
                "position": "1",
                }
        }
}

api

class Admin::Items::UpdateService < ActiveInteraction::Base
  string :name, default: nil
  integer :id, default: nil
  array :images, default: []
​
  validates :name, presence: truedef execute
    ActiveRecord::Base.transaction do
      item = Item.find(:id)
      item.images.each do |image|
        # clientから送られてきたimages(json画像リスト)にあって、
        # itemに紐づくitem.images(db画像リスト)ないやつは
        next if images.pluck(:id).include?(image.id)
        # 削除する
        image.destroy!
      end
​
      images.each do |image|
        exist_image = item.images.to_a.find { |i| i.id == image[:id] }
        if exist_image
	# dbにすでに存在する画像はpositionのみ更新する
          exist_image.update!(position: image[:position])
        else
	# dbにないやつは作成
          item_image = item.images.build(position: image[:position])
          item_image.image.attach(data: image[:image])
          item_image.save!
        end
      end
      item
    end
  rescue ActiveRecord::RecordInvalid => e
    errors.merge!(e.record.errors)
  endend

かなりざっくりの説明になってしまいましたが、こんな感じでやりたいこと

  • 画像の登録や並び替えや削除をしたい
  • でも登録や削除や並び替えの際に毎回serverにリクエストを送りたくない
  • 更新ボタンを押す等、一回のリクエストで済ませたい

が実現できました🎉

小話

dnd-kitのissueで少しだけ貢献することができました!!

スタンプ初めてもらってすごく嬉しかったです!

https://github.com/clauderic/dnd-kit/issues/355#issuecomment-1594711324

ではまた明日!

Discussion