シェアフル Advent Calendar 2018 12日目の記事です。
webならドラッグ & ドロップでアイテム順番を入れ替えるけど、react-nativeならどうするのかなー と思いググったらreact-nativeでsortable listを実装しているライブラリがあったので、今回はそのライブラリを紹介します
sortableとは
ドラッグ & ドロップで要素を入れ替えるUIを指します。 順番入れ替えを実装する際に利用されるUIで、直感的な操作を提供できます
react-nativeでの実装
調べるといくつかライブラリが出てきますが、今回は以下のライブラリで実装してみた
選定理由は以下の通り
- Expo Snackに動作するサンプルがある snack.expo.io
- TypeScriptの型が提供されている www.npmjs.com
実装してみた
現状、こんな感じ

並び替えに行くまでの導線が長いので、まだまだ改善の余地はあるけど、実装したかったことはできたので一旦良しとしよう
コード
ほぼExampleのコピペですが、大体こんな感じで動くコードが書けた。
■ Cards.tsx
import React, { Component } from "react";
import SortableList from "react-native-sortable-list";
import getKind from "../../../lib/getKind";
import Card from "../../molecules/Schedule/Card";
import Row from "./Row";
type DataKey = string | number;
export interface ItemProps {
id: string;
title: string;
moveMinutes: number | null;
}
export interface Props {
data: ItemProps[];
onChange: (data: any) => void;
}
export interface RowProps {
data: ItemProps;
active: boolean;
}
export default class extends Component<Props> {
renderItem({ data, active }: { data: ItemProps; active: boolean }) {
return (
<Row active={active}>
<Card id={data.id} title={data.title} kind={getKind(data.title)} />
</Row>
);
}
onChange = (nextOrder: DataKey[]) => {
const data = nextOrder.map(id => {
return this.props.data.find(item => Number(item.id) === Number(id));
});
this.props.onChange(data);
};
render() {
const obj = this.props.data.reduce((o, c) => ({ ...o, [c.id]: c }), {});
return (
<SortableList
data={obj}
renderRow={this.renderItem.bind(this)}
style={{ flex: 1 }}
onChangeOrder={this.onChange}
/>
);
}
}
■ Row.tsx
import React, { Component } from "react";
import { Animated, Easing, Platform, View } from "react-native";
export interface RowProps {
active: boolean;
children: any;
}
export default class extends Component<RowProps> {
_active: any;
_style: any;
constructor(props: RowProps) {
super(props);
this._active = new Animated.Value(0);
this._style = {
...Platform.select({
ios: {
transform: [
{
scale: this._active.interpolate({
inputRange: [0, 1],
outputRange: [1, 1.1]
})
}
],
shadowRadius: this._active.interpolate({
inputRange: [0, 1],
outputRange: [2, 10]
})
},
android: {
transform: [
{
scale: this._active.interpolate({
inputRange: [0, 1],
outputRange: [1, 1.07]
})
}
],
elevation: this._active.interpolate({
inputRange: [0, 1],
outputRange: [2, 6]
})
}
})
};
}
componentDidUpdate(prevProps: RowProps) {
if (this.props.active !== prevProps.active) {
Animated.timing(this._active, {
duration: 300,
easing: Easing.bounce,
toValue: Number(this.props.active)
}).start();
}
}
render() {
return (
<Animated.View style={[this._style]}>
<View style={{ paddingBottom: 50 }}>{this.props.children}</View>
</Animated.View>
);
}
}
最後に
ドラッグ & ドロップはreact-dndという魔境ライブラリの思い出があったので手を出していなかったけど、「react-native-sortable-list」は使い方が限定されているので、実装しやすかった。