Material-UI + react-smooth-dnd でドラッグ&ドロップ可能なリストを作成する
2021年10月7日追記:初版の記事タイトルが誤っているというご指摘を受けたため、修正いたしました。
こんにちは、Gaji-Labo アシスタントエンジニアの石垣です。
今回は、React 用 UI フレームワークである Material-UI と、ドラッグ&ドロップ可能なリストを作成できるライブラリ react-smooth-dnd でリストを実装する機会があったため、その実装方法についてまとめてみようと思います。
概要
Material Design のコンポーネントの一つにドラッグ&ドロップ可能なリストが定義されていますが、Material-UI にはデフォルトではそのコンポーネントは用意されていません。
そのため、別途プラグインを導入して独自に実装する必要がありました。
いくつかプラグインの候補がありましたが、今回は以下の条件を満たしているかどうかでプラグインを決めました。
- ある程度プラグイン自体がアニメーションなどの機能を持っており、実装の負担が少ない
- 軸の固定を行うかなど、設定が充実している
react-beautiful-dnd
では軸の固定がプラグインのみでは実現できなかったため、候補から外しました。
- ドラッグ時に元のコンポーネントのスタイルが保持される
react-sortable-hoc
ではドラッグ時にスタイルの付いていないDOMが複製され、見た目が崩れてしまうため候補から外しました。
結果的に、react-smooth-dnd
というパッケージを導入して実装することにしました。
実装方法
1. 基本となるコンポーネントを作成
まず、配列を受け取りコンポーネントを表示するコンポーネントを実装しました。
import React, { useState, ReactElement } from "react";
import { Box, TextField } from "@material-ui/core/";
import DragHandleIcon from "@material-ui/icons/DragHandle";
export function DraggableInputContainer(): ReactElement | null {
const [items, setItems] = useState([
{
content: <TextField label="TextField 1" variant="outlined" />,
},
{
content: <TextField label="TextField 2" variant="outlined" />,
},
{
content: <TextField label="TextField 3" variant="outlined" />,
},
]);
return (
<>
{items.map(({ content }, i) => {
return (
<Box
display="flex"
justifyContent="space-between"
alignItems="center"
padding="8px"
key={`DraggableInputContainer-${i}`}
>
{content}
<DragHandleIcon />
</Box>
);
})}
</>
);
}
配列の中身には TextField
コンポーネントを入れています。
ドラッグ用にアイコンを追加していますが、この時点ではまだドラッグで移動させることはできません。
2. react-smooth-dnd
のコンポーネントを追加
import React, { useState, ReactElement } from "react";
...
import { Container, Draggable, DropResult } from "react-smooth-dnd";
import arrayMove from "array-move"; // 配列移動に使用するライブラリ
export function DraggableInputContainer(): ReactElement | null {
const [items, setItems] = ...
const onDrop = (dropResult: DropResult) => { // `DropResult` で型定義
const { removedIndex, addedIndex } = dropResult;
setItems((itemsArray) =>
arrayMove(itemsArray, removedIndex || 0, addedIndex || 0)
);
};
return (
<Container
dragHandleSelector=".dragHandleSelector" // ドラッグ用コンポーネントのセレクタを指定
lockAxis="y" // 軸の固定を指定
onDrop={onDrop} // ドラッグ時に呼ばれる関数、dropResult が渡る
>
{items.map(({ content }, i) => {
return (
<Draggable key={`DraggableInputContainer-${i}`}>
<Box
display="flex"
justifyContent="space-between"
alignItems="center"
padding="8px"
>
{content}
<DragHandleIcon className="dragHandleSelector" />
</Box>
</Draggable>
);
})}
</Container>
);
}
Container
コンポーネントで全体を囲み、その中の移動させたい要素を Draggable
コンポーネントで囲みます。Container
コンポーネントの子には Draggable
しか追加できない点に注意が必要です。
onDrop
で配列の移動時に呼ばれる関数を定義することで、ドラッグ&ドロップ可能なリストを作成することができます。
dragHandleSelector
lockAxis
onDrop
以外の props については公式のドキュメントを参照ください。
以上でドラッグ&ドロップ可能なリストの実装をすることができました。
まとめ
今回は、React 用 UI フレームワークである Material-UI と、react-smooth-dnd を使用してドラッグ&ドロップ可能なリストを実装する方法についてまとめました。
ドラッグ&ドロップ可能なリストの実装の際に参考にしていただければと思います。
Gaji-Laboでは、JavaScriptフレームワーク経験が豊富なパートナーさんを募集しています
Gaji-Laboでは、開発チームの一員としてプロジェクトに一緒に取り組んでくれる業務委託のパートナーさんを募集しています。
現在は特にJavaScriptフレームワーク実践と業務経験が豊富なWebフロントエンドエンジニアを必要としています。React + TypeScript、Vue.js、Next.js、Nuxt.js など、あなたの得意なフレームワークを教えて下さい!
パートナー契約へのお問い合わせもお仕事へのお問い合わせも、どちらもいつでも大歓迎です。まずはオンラインでのリモート面談からはじめましょう。ぜひお気軽にお問い合わせください!
お問い合わせしてみる!