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-sortablejs
Contains no external dependencies, only peer dependencies:
react
:^17.0.0 || ^18.0.0
sortablejs
:^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[]>>
, whereT
is your item type. In most cases this should be asetState
function returned from ReactuseState
hook.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 createdSortable
object - set tonull
on 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 uniquekey
prop (NOT list index). setItems
function should cause rerender of sortable list to reflect items state.
Behaviour is undefined if any of these constraints is not met.