useDragToReorder
Lift a row and drag it to a new position in a list.
Draft
The API shown here is conceptual and may change.
The hook behind Drag-to-reorder. That page explains how the drag starts, the target slot, auto-scroll, the drop and accessibility. This one covers the API.
Installation
npx axiom add use-drag-to-reorderpnpm dlx axiom add use-drag-to-reorderyarn dlx axiom add use-drag-to-reorderbun x axiom add use-drag-to-reorderInstalls: react-native-reanimated, react-native-gesture-handler.
Usage
The hook owns the gesture state. ReorderableRow moves each row, DragHandle starts the drag.
import {
useDragToReorder,
ReorderableRow,
DragHandle,
} from "@/hooks/use-drag-to-reorder";
function Queue() {
const [tracks, setTracks] = useState(initialTracks);
const reorder = useDragToReorder({
data: tracks,
keyExtractor: (track) => track.id,
onReorder: (from, to) => setTracks((list) => move(list, from, to)),
});
return (
<Animated.ScrollView {...reorder.scrollProps}>
{tracks.map((track, index) => (
<ReorderableRow key={track.id} control={reorder} index={index}>
<Item trailing={<DragHandle control={reorder} index={index} />}>
…
</Item>
</ReorderableRow>
))}
</Animated.ScrollView>
);
}move returns a new array with the item at from moved to to. The hook ships one:
export const move = <T>(list: T[], from: number, to: number) => {
const next = [...list];
next.splice(to, 0, next.splice(from, 1)[0]);
return next;
};Without handles
Drop DragHandle and set trigger: 'longPress'. The whole row starts the drag after longPressDelay, and a tap still reaches the row’s onPress.
const reorder = useDragToReorder({
data,
keyExtractor,
onReorder,
trigger: "longPress",
});Saving the order
onReorder runs once per drop. Update local state there, and send the new order to the server from it or after a debounce; if the request fails, restore the previous array.
Options
| Option | Type | Default | Description |
|---|---|---|---|
data | T[] | — | Items in their current order. |
keyExtractor | (item: T) => string | — | Stable key, used to keep measurements across re-renders. |
onReorder | (from: number, to: number) => void | — | Called on drop when the position changed. |
trigger | 'handle' | 'longPress' | 'handle' | What starts the drag. |
longPressDelay | number | 300 | Delay in ms with trigger: 'longPress'. |
autoScrollEdge | number | 60 | Distance in pt from the list’s edges where auto-scroll starts. |
autoScrollSpeed | number | 600 | Maximum auto-scroll speed in pt/s, at the very edge. |
onDragStart / onDragEnd | (index: number) => void | — | Called when a row lifts and when it’s dropped. Default: a light haptic on start. |
enabled | boolean | true | Turns reordering off. |
Return value
| Key | Type | Description |
|---|---|---|
activeIndex | SharedValue<number> | Index of the lifted row, -1 when idle. |
targetIndex | SharedValue<number> | Slot the row would drop into. |
translateY | SharedValue<number> | Offset of the lifted row from its slot, in pt. |
dragging | boolean | true while a row is lifted. React state. |
moveItem | (from: number, to: number) => void | Moves without a gesture, for the accessibility actions. Calls onReorder. |
scrollProps | object | Ref, scroll handler and native gesture, for an Animated.ScrollView or Animated.FlatList. |