Replies: 1 comment
-
|
import { Reorder } from 'motion/react'
import { useRef, useState } from 'react'
function MyList({ initial }: { initial: Item[] }) {
const [items, setItems] = useState(initial)
const latestOrderRef = useRef(items)
// Keep a ref in sync with state so the drag-end handler sees the newest order.
latestOrderRef.current = items
const persist = async () => {
await fetch('/api/items/reorder', {
method: 'POST',
body: JSON.stringify({ ids: latestOrderRef.current.map((i) => i.id) }),
})
}
return (
<Reorder.Group axis="y" values={items} onReorder={setItems}>
{items.map((item) => (
<Reorder.Item
key={item.id}
value={item}
onDragEnd={persist} // ← the reliable "drop" signal
>
{item.name}
</Reorder.Item>
))}
</Reorder.Group>
)
}Three subtleties worth knowing about1. Guard against no-op drops. Users sometimes start a drag, change their mind, and drop in the same spot. You probably don't want to fire an API call for that: const snapshotRef = useRef<string>('')
<Reorder.Item
key={item.id}
value={item}
onDragStart={() => { snapshotRef.current = items.map((i) => i.id).join(',') }}
onDragEnd={() => {
const next = latestOrderRef.current.map((i) => i.id).join(',')
if (next !== snapshotRef.current) persist()
}}
>2. Rollback on failure. The optimistic UI in the snippet above assumes success. Proper rollback means remembering the pre-drag order: const rollbackRef = useRef<Item[]>(items)
<Reorder.Item
onDragStart={() => { rollbackRef.current = items }}
onDragEnd={async () => {
try {
await fetch('/api/items/reorder', { /* ... */ })
} catch {
setItems(rollbackRef.current) // revert
}
}}
>3. Debounce if you also want intermediate saves. If the list is huge and the drag is long, you sometimes want to save intermediate positions so a crash mid-drag doesn't lose progress. import { useMemo } from 'react'
import { debounce } from 'es-toolkit'
const debouncedPersist = useMemo(() => debounce(persist, 300), [])
<Reorder.Group
values={items}
onReorder={(next) => {
setItems(next)
debouncedPersist()
}}
>
{items.map((item) => (
<Reorder.Item
value={item}
onDragEnd={() => {
debouncedPersist.cancel()
persist() // final flush
}}
/>
))}
</Reorder.Group>Most apps don't need this — the simple Why not
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
So Reorder.Group.onReorder method calls every time item changes its position during the drag. But that doesn't mean that DnD is finished. If I continue drag from the beginning of the list to its end - I will see multiple onReorder calls. So, I cannot use onReorder as a trigger for issues a api request call.
My question is: how to reliably detect that item reached its final destination after all reorder events, and dropped, so I can persist new state of elements on the server via api call?
Thanks.
Beta Was this translation helpful? Give feedback.
All reactions