react-native
で並び替え可能なlistを簡単に作れるreact-native-draggable-flatlist
使っていますか??
(https://github.com/computerjazz/react-native-draggable-flatlist)
便利なんですが、表示がバグる問題に遭遇したのでメモがてら共有しようと思います。
drag後に一瞬だけdrag前の順序で表示されてしまう
発生していた問題は上の通りです。
DraggableFlatlist
自体はこんな使い方をすると思います
<DraggableFlatlist
data={flatlistData}
onDragEnd={onDragEnd}
keyExtractor={(item): string => item.id}
renderItem={renderItem}
/>
onDragEndではAPIで順序を更新して、更新ができればStateにも反映するというような処理をしていました。
詳しくは書きませんがだいたいイメージとしてこんな感じです。
const onDragEnd = async ({ data }: DragEndParams<MockInterview>): Promise<void> => {
// DB上で順序を保持するcolumnを更新、更新後のdataを返すapi
const result = await api(data)
// DraggableFlatlistの表示を更新するためにdataとして渡しているflatlistDataのStateを更新
setFlatlistData(result)
}
これでdragすればDB更新しつつ表示も順序通りにできると思っていたのですが、
いざ動かすと、一瞬動かす前の順番に戻ってしまう状態に陥りました。
原因: 最初にdataをsetする必要があった
結論から言うと、onDragEnd内でdataとして渡しているStateを更新するときは、一番最初にとりあえずとして更新しておかないといけないようです。
最初に更新しないと、通信の結果を待っている間は更新前、つまりdrag前のStateの順序で表示がされるみたいです。
なので解決としては、onDragEndのparamsのdataは並び替えた順序通りになっているので、
const onDragEnd = async ({ data }: DragEndParams<MockInterview>): Promise<void> => {
// 暫定的にDBの更新が成功するかに関わらずdataの内容で順序を保証
setFlatlist(data)
// DB上で順序を保持するcolumnを更新、更新後のdataを返すapi
const result = await api(data)
// DBの更新結果を反映
setFlatlistData(result)
}
といった感じで、最初に暫定的に更新してしまうという対応ができる。
今回の場合は通信やDBの更新が失敗する可能性が考えられるので、実際の更新結果を待ってState更新する必要がありました。その場合でもとりあえずはうまくいった場合の内容でStateを更新しておく必要があるというわけです。
もちろんdataとresultと2回のset内容が違う場合は当然表示が切り替わることになります。
しかしdataとresultが異なる状況は、DBなどで更新に失敗したということになるので、DBと表示を合わせるために切り替わって問題ないでしょう。
Toastなどで更新失敗してしまったことを伝えるとUX的にもいいでしょう。
余談: 根本原因はたぶんこれ??
非同期の関数だからかもしれない
Readme見ると、onDragEndの返り値はPromise<void>
ではなくvoid
なのでそもそも今回のような非同期関数での利用を想定していないっぽい
(https://github.com/computerjazz/react-native-draggable-flatlist#:~:text=(params%3A%20%7B%20data%3A%20T%5B%5D%2C%20from%3A%20number%2C%20to%3A%20number%20%7D)%20%3D%3E%20void)
当然ソースコードを見てもonDragEnd周辺でawaitしたりしていないので、
- 一旦非同期のonDragEndの処理を無視して元のStateのまま表示
- そのあとonDragEnd内でsetStateされると新しいStateの順序で表示
ということになっていそう
それでも処理の最初くらいすぐにsetStateすれば1が起きずに済むということなのかと
api通信くらい待ち時間が生じると1が起きてしまう感じと予想
確証がないので余談としました。