Skip to content

Instantly share code, notes, and snippets.

@AdithyaBhat17
Created July 27, 2021 05:38
Show Gist options
  • Save AdithyaBhat17/6a2056c8fcd54f8db659bcb036685261 to your computer and use it in GitHub Desktop.
Save AdithyaBhat17/6a2056c8fcd54f8db659bcb036685261 to your computer and use it in GitHub Desktop.
Drag and sort react hook
import { useCallback, useState } from 'react';
export const initialState = {
draggedFrom: null,
draggedTo: null,
isDragging: false,
defaultOrder: [],
updatedOrder: []
};
/**
* A custom hook to listen to drag and drop events and sort list items
* @param {{isPersistent?: boolean, defaultOrder: string[] | number[]}}
* @returns {{sortOrder: any[], draggedFrom: number | null, draggedTo: number | null, isDragging: boolean, defaultOrder: string[] | number[], updatedOrder: string[] | number[], onDragStart: (event: React.DragEvent<HTMLDivElement>) => void, onDrop: () => void, onDragOver: (event: React.DragEvent<HTMLDivElement>) => void}}
* @author Adithya NR
*/
export default function useDraggable({
isPersistent = false,
defaultOrder = []
}) {
const [dnd, setDnD] = useState(initialState);
const persistedOrder = JSON.parse(sessionStorage.getItem('sortOrder'));
const [sortOrder, setSortOrder] = useState(persistedOrder || defaultOrder);
/**
*
* @param {React.DragEvent<HTMLDivElement>} event
*/
const onDragOver = useCallback(
event => {
// prevent default behaviour of dragover to block dropping list item
event.preventDefault();
// eslint-disable-next-line no-param-reassign
event.dataTransfer.dropEffect = 'move';
// initialize a new list
let newList = dnd.defaultOrder;
const { draggedFrom } = dnd;
// get draggedTo position from data-position
const draggedTo = Number(event.currentTarget.dataset.position);
// fetch the dragged item from newList
const itemDragged = newList[draggedFrom];
// find remaining idle items.
const idleItems = newList.filter((_i, index) => index !== draggedFrom);
// update newList order
newList = [
...idleItems.slice(0, draggedTo),
itemDragged,
...idleItems.slice(draggedTo)
];
// if element was dropped to its origin, we don't update the state
if (draggedTo !== dnd.draggedTo) {
setDnD({
...dnd,
updatedOrder: newList,
draggedTo
});
}
},
[dnd]
);
/**
* @param {React.DragEvent<HTMLDivElement>} event
*/
const onDragStart = useCallback(
event => {
// fetch initial position from data-position
const initialPosition = Number(event.currentTarget.dataset.position);
event.dataTransfer.effectAllowed = 'move';
setDnD({
...dnd,
draggedFrom: initialPosition,
isDragging: true,
defaultOrder: sortOrder
});
// for firefox
event.dataTransfer.setData('text/html', '');
},
[dnd, sortOrder]
);
const onDrop = useCallback(() => {
// once the item is dropped, we can now sort the list and clean up the state
setSortOrder(dnd.updatedOrder);
if (isPersistent) {
sessionStorage.setItem('sortOrder', JSON.stringify(dnd.updatedOrder));
}
setDnD({
...dnd,
draggedFrom: null,
draggedTo: null,
isDragging: false
});
}, [dnd, isPersistent]);
return {
sortOrder,
...dnd,
onDragStart,
onDragOver,
onDrop
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment