SortableJS ported to React hooks.
Demo: https://harrel56.github.io/use-sortablejs
Created to serve as a refreshed alternative to react-sortablejs, with hook design inspired by @mui/base.
Currently, package is only available as ES module.
npm i use-sortablejsContains no external dependencies, only peer dependencies:
react:^17.0.0 || ^18.0.0sortablejs:^1.0.0@types/react:^17.0.0 || ^18.0.0@types/sortablejs:^1.0.0
Package exports:
SortableProvider: sortable context provider,useSortable: main hook which requires access to sortable context- and typescript definitions.
Supports:
- all basic functionalities from
SortableJS, - swap plugin (you have to mount it yourself)
- and multidrag plugin (you have to mount it yourself).
Before using useSortable hook, it's required to wrap your application with SortableProvider.
Preferably, there should be only one SortableProvider per whole application, but it's not mandatory.
Nevertheless, interactions between two sortables in separate contexts have undefined behaviour.
Example:
// App.tsx
import {SortableProvider} from 'use-sortablejs'
import List from './List'
const App = () => {
return (
<SortableProvider>
<List/>
</SortableProvider>
)
}// List.tsx
import {useState} from 'react'
import {useSortable} from 'use-sortablejs'
const List = () => {
const [items, setItems] = useState([
'Item 1',
'Item 2',
'Item 3',
'Item 4',
'Item 5'
])
const {getRootProps, getItemProps} = useSortable({setItems, options: {animation: 150}})
return (
<div {...getRootProps()}>
{items.map(item => <div key={item} {...getItemProps(item)}>{item}</div>)}
</div>
)
}Where item type can be possibly anything (primitive, object of any shape, function).
All types definitions can be found in this file.
useSortable takes UseSortableProps parameter, which is an object containing:
setItems:Dispatch<SetStateAction<T[]>>, whereTis your item type. In most cases this should be asetStatefunction returned from ReactuseStatehook.options:ExtendedOptions<T>, options object which you would normally pass toSortable.create().- (optional)
cloneItem:(item: T) => T, clone function to perform when item is being cloned. Defaults to internal shallow clone function. - (optional)
sortableRef:LegacyRef<Sortable>, ref object or ref callback, which will be set/called with createdSortableobject - set tonullon dismount.
Additionally, all event functions that you pass to options object will have access to extended event object (SortableEventExtended<T>),
which contains additional field stateItem, which corresponds to dragged item state and is directly mapped from item field.
Leveraging options reactivity is the preferred way of achieving dynamic changes to Sortable object, but if you need more control sortableRef is the way to go.
const myRef = useRef<Sortable>(null)
const {getRootProps, getItemProps} = useSortable({setItems, sortableRef: myRef})const myCallbackRef = (sortable: Sortable | null) => {
sortable?.option('sort', false)
}
const {getRootProps, getItemProps} = useSortable({setItems, sortableRef: myCallbackRef})- Each direct child of node with
getRootProps()should have set props fromgetItemProps(item). - Each direct child of node with
getRootProps()should contain uniquekeyprop (NOT list index). setItemsfunction should cause rerender of sortable list to reflect items state.
Behaviour is undefined if any of these constraints is not met.