diff --git a/src/components/ReorderableList/ReorderableList.tsx b/src/components/ReorderableList/ReorderableList.tsx index 008aff8..42d9237 100644 --- a/src/components/ReorderableList/ReorderableList.tsx +++ b/src/components/ReorderableList/ReorderableList.tsx @@ -45,7 +45,6 @@ const ReorderableList = ( itemHeight, dragY, draggedIndex, - releasedIndex, duration, } = useReorderableList({ ref, @@ -70,20 +69,11 @@ const ReorderableList = ( itemHeight={itemHeight} dragY={dragY} draggedIndex={draggedIndex} - releasedIndex={releasedIndex} animationDuration={duration} startDrag={startDrag} /> ), - [ - itemOffset, - itemHeight, - dragY, - draggedIndex, - releasedIndex, - duration, - startDrag, - ], + [itemOffset, itemHeight, dragY, draggedIndex, duration, startDrag], ); return ( diff --git a/src/components/ReorderableList/useReorderableList.ts b/src/components/ReorderableList/useReorderableList.ts index b1f8113..a1dbc04 100644 --- a/src/components/ReorderableList/useReorderableList.ts +++ b/src/components/ReorderableList/useReorderableList.ts @@ -79,8 +79,10 @@ export const useReorderableList = ({ const previousIndex = useSharedValue(-1); const currentIndex = useSharedValue(-1); const draggedIndex = useSharedValue(-1); - const releasedIndex = useSharedValue(-1); const state = useSharedValue(ReorderableListState.IDLE); + const dragEndHandlers = useSharedValue< + ((from: number, to: number) => void)[][] + >([]); // animation duration as a shared value allows to avoid re-rendering of all cells on value change const duration = useSharedValue(animationDuration); @@ -93,8 +95,9 @@ export const useReorderableList = ({ draggedHeight, currentIndex, draggedIndex, + dragEndHandlers, }), - [draggedHeight, currentIndex, draggedIndex], + [draggedHeight, currentIndex, draggedIndex, dragEndHandlers], ); const startY = useSharedValue(0); @@ -157,18 +160,13 @@ export const useReorderableList = ({ const resetSharedValues = useCallback(() => { 'worklet'; - // must be reset before the reorder function is called - // to avoid triggering on drag end event twice, - // update with a delay to avoid reanimated batching the - // change if updated immediately - releasedIndex.value = withDelay(1, withTiming(-1, {duration: 0})); draggedIndex.value = -1; // current index is reset on item render for the on end event dragY.value = 0; // released flag is reset after release is triggered in the item state.value = ReorderableListState.IDLE; dragScrollTranslationY.value = 0; - }, [releasedIndex, draggedIndex, dragY, state, dragScrollTranslationY]); + }, [draggedIndex, dragY, state, dragScrollTranslationY]); const reorder = (fromIndex: number, toIndex: number) => { runOnUI(resetSharedValues)(); @@ -283,12 +281,17 @@ export const useReorderableList = ({ state.value === ReorderableListState.AUTO_SCROLL) ) { state.value = ReorderableListState.RELEASING; - releasedIndex.value = draggedIndex.value; // enable back scroll on releasing runOnJS(setScrollEnabled)(true); // trigger onDragEnd event - onDragEnd?.({from: draggedIndex.value, to: currentIndex.value}); + let e = {from: draggedIndex.value, to: currentIndex.value}; + onDragEnd?.(e); + + const handlers = dragEndHandlers.value[draggedIndex.value]; + if (Array.isArray(handlers)) { + handlers.forEach(fn => fn(e.from, e.to)); + } // they are actually swapped on drag translation const currentItemOffset = itemOffset.value[draggedIndex.value]; @@ -488,7 +491,6 @@ export const useReorderableList = ({ itemOffset, itemHeight, draggedIndex, - releasedIndex, dragY, duration, }; diff --git a/src/components/ReorderableListCell.tsx b/src/components/ReorderableListCell.tsx index 59cf62e..8167409 100644 --- a/src/components/ReorderableListCell.tsx +++ b/src/components/ReorderableListCell.tsx @@ -21,7 +21,6 @@ interface ReorderableListCellProps itemHeight: SharedValue; dragY: SharedValue; draggedIndex: SharedValue; - releasedIndex: SharedValue; // animation duration as a shared value allows to avoid re-renders on value change animationDuration: SharedValue; } @@ -36,7 +35,6 @@ export const ReorderableListCell = memo( itemHeight, dragY, draggedIndex, - releasedIndex, animationDuration, }: ReorderableListCellProps) => { const dragHandler = useCallback(() => { @@ -50,9 +48,8 @@ export const ReorderableListCell = memo( index, dragHandler, draggedIndex, - releasedIndex, }), - [index, dragHandler, draggedIndex, releasedIndex], + [index, dragHandler, draggedIndex], ); const {currentIndex, draggedHeight} = useContext(ReorderableListContext); diff --git a/src/contexts/ReorderableCellContext.ts b/src/contexts/ReorderableCellContext.ts index 6570164..4bcbe65 100644 --- a/src/contexts/ReorderableCellContext.ts +++ b/src/contexts/ReorderableCellContext.ts @@ -6,7 +6,6 @@ interface ReorderableCellContextData { index: number; dragHandler: () => void; draggedIndex: SharedValue; - releasedIndex: SharedValue; } export const ReorderableCellContext = React.createContext< diff --git a/src/contexts/ReorderableListContext.ts b/src/contexts/ReorderableListContext.ts index 4cb1b15..5096b21 100644 --- a/src/contexts/ReorderableListContext.ts +++ b/src/contexts/ReorderableListContext.ts @@ -5,6 +5,7 @@ import type {SharedValue} from 'react-native-reanimated'; interface ReorderableListContextData { currentIndex: SharedValue; draggedHeight: SharedValue; + dragEndHandlers: SharedValue<((from: number, to: number) => void)[][]>; } export const ReorderableListContext = React.createContext< diff --git a/src/hooks/useReorderableDragEnd.ts b/src/hooks/useReorderableDragEnd.ts index 7a3d415..3f7c694 100644 --- a/src/hooks/useReorderableDragEnd.ts +++ b/src/hooks/useReorderableDragEnd.ts @@ -1,4 +1,4 @@ -import {useAnimatedReaction} from 'react-native-reanimated'; +import {useEffect} from 'react'; import {useContext} from './useContext'; import {ReorderableCellContext, ReorderableListContext} from '../contexts'; @@ -6,16 +6,32 @@ import {ReorderableCellContext, ReorderableListContext} from '../contexts'; export const useReorderableDragEnd = ( onEnd: (from: number, to: number) => void, ) => { - const {currentIndex} = useContext(ReorderableListContext); - const {releasedIndex, index} = useContext(ReorderableCellContext); - - useAnimatedReaction( - () => releasedIndex.value === index, - newValue => { - if (newValue) { - onEnd(index, currentIndex.value); + const {dragEndHandlers} = useContext(ReorderableListContext); + const {index} = useContext(ReorderableCellContext); + + useEffect(() => { + dragEndHandlers.modify(value => { + 'worklet'; + + if (!Array.isArray(value[index])) { + value[index] = []; } - }, - [onEnd], - ); + + value[index].push(onEnd); + + return value; + }); + + return () => { + dragEndHandlers.modify(value => { + 'worklet'; + + if (Array.isArray(value[index])) { + value[index] = value[index].filter(x => x.name !== onEnd.name); + } + + return value; + }); + }; + }, [index, dragEndHandlers, onEnd]); };