React — Swap two items
Use swap in React to exchange only two items and inspect the completion event.
Different from insertion sorting
Section titled “Different from insertion sorting”Dropping A on the center of C in [A, B, C, D] produces [C, B, A, D]. Only the two items exchange positions; B and D remain. Intervening items are not shifted as they would be for insertion.
- Drag the A handle to the center of C and release.
- Check JSON for
status: "dropped",change.operation: "swap",change.itemId: "A", andchange.swapItemId: "C". - Start another drag and cancel with Escape; order remains unchanged.
- Reset returns to A, B, C, D.
Public options
Section titled “Public options”| Option | Value/purpose |
|---|---|
swap |
true — exchanges source and target within the same area. |
direction |
"vertical" — this example uses one vertical list. |
itemKey or data-sortable-id |
Supplies identity retained across movement. |
handle |
".task-handle" — restricts pointer input to handles. |
React state commit
Section titled “React state commit”SortableRoot → SortableArea · items / onItemsChange
Commit the adapter’s next array to state immediately. Do not mutate the original array or use index-based keys. Check both the onAfterDrag completion result and the displayed order.
This is a same-area, single-item swap. Grid collision, multiDrag, threshold/invertSwap combinations, and cross-area delivery remain separate scenarios. This pointer-based example does not claim full keyboard reordering simply because its handle is focusable.
Related documentation
Section titled “Related documentation”Advanced sorting guide · Core lifecycle · Vanilla instance · Ordinary list transfer
Try it live
Section titled “Try it live”import { useState } from 'react';import { SortableRoot, SortableArea } from 'comins-sortable/react';import type { AfterDragResult } from 'comins-sortable/core';import 'comins-sortable/styles.css';type Item = { id: string };const initial: Item[] = ['A', 'B', 'C', 'D'].map(id => ({ id }));export default function ReactSwap({ lang = 'ko' }: { lang?: string }) { const ko = lang === 'ko'; const [items, setItems] = useState(initial); const [result, setResult] = useState<AfterDragResult | null>(null); return <div className="demo" data-demo="swap-react"> <div className="demo-toolbar"><span>{ko ? 'A의 손잡이를 C의 중앙에 놓으십시오.' : 'Drop the A handle onto the center of C.'}</span><button onClick={() => { setItems(initial); setResult(null); }}>{ko ? '초기화' : 'Reset'}</button></div> <SortableRoot<Item> onAfterDrag={setResult}> <SortableArea<Item> areaId="items" items={items} itemKey="id" swap direction="vertical" handle=".task-handle" onItemsChange={next => setItems([...next])} areaProps={{ className: 'task-list swap-list' }}> {item => <div className="task" data-swap-id={item.id}><button className="task-handle" aria-label={`Drag ${item.id}`}>⠿</button><span>{item.id}</span></div>} </SortableArea> </SortableRoot> <p role="status" data-swap-order>{items.map(item => item.id).join(', ')}</p> <pre data-swap-result>{JSON.stringify(result, null, 2)}</pre> </div>;}