Skip to content

Commit ad03d2b

Browse files
authored
fix: Invalid custom handle measurement (#384)
## Description This PR fixes issues caused by recent changes introduced in the #377 PR. I modified the gesture in the custom handle component by adding the `onBegin` method to the gesture. This didn't work when the custom handle was rendered after the item (e.g. with some timeout). This PR fixes this invalid behavior and simplifies handle measurement logic a bit.
1 parent 0b466ac commit ad03d2b

File tree

6 files changed

+137
-166
lines changed

6 files changed

+137
-166
lines changed

packages/react-native-sortables/src/components/shared/CustomHandle.tsx

Lines changed: 16 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
import { type PropsWithChildren, useCallback, useEffect, useMemo } from 'react';
1+
import { type PropsWithChildren, useCallback, useEffect } from 'react';
22
import { View } from 'react-native';
33
import { GestureDetector } from 'react-native-gesture-handler';
4-
import { measure, useAnimatedRef } from 'react-native-reanimated';
4+
import { useAnimatedRef } from 'react-native-reanimated';
55

66
import {
7-
useCommonValuesContext,
87
useCustomHandleContext,
9-
useDragContext,
108
useItemContext,
119
usePortalOutletContext
1210
} from '../../providers';
@@ -49,85 +47,27 @@ function CustomHandleComponent({
4947
);
5048
}
5149

52-
const { activeItemKey, containerRef, itemPositions } =
53-
useCommonValuesContext();
54-
const { setDragStartValues } = useDragContext();
55-
const { gesture, itemKey } = useItemContext();
56-
const viewRef = useAnimatedRef<View>();
50+
const { gesture, isActive, itemKey } = useItemContext();
51+
const handleRef = useAnimatedRef<View>();
5752

58-
const {
59-
activeHandleMeasurements,
60-
activeHandleOffset,
61-
makeItemFixed,
62-
removeFixedItem
63-
} = customHandleContext;
53+
const { registerHandle, updateActiveHandleMeasurements } =
54+
customHandleContext;
6455
const dragEnabled = mode === 'draggable';
6556

6657
useEffect(() => {
67-
if (mode === 'fixed') {
68-
makeItemFixed(itemKey);
69-
}
70-
71-
return () => removeFixedItem(itemKey);
72-
}, [mode, itemKey, makeItemFixed, removeFixedItem]);
73-
74-
const measureHandle = useCallback(
75-
(mustBeActive: boolean) => {
76-
'worklet';
77-
if (mustBeActive && activeItemKey.value !== itemKey) {
78-
return;
79-
}
80-
81-
const handleMeasurements = measure(viewRef);
82-
const containerMeasurements = measure(containerRef);
83-
const itemPosition = itemPositions.value[itemKey];
84-
85-
if (!handleMeasurements || !containerMeasurements || !itemPosition) {
86-
return;
87-
}
88-
89-
const { pageX, pageY } = handleMeasurements;
90-
const { pageX: containerPageX, pageY: containerPageY } =
91-
containerMeasurements;
92-
const { x: activeX, y: activeY } = itemPosition;
58+
return registerHandle(itemKey, handleRef, mode === 'fixed');
59+
}, [handleRef, itemKey, registerHandle, mode]);
9360

94-
activeHandleMeasurements.value = handleMeasurements;
95-
activeHandleOffset.value = {
96-
x: pageX - containerPageX - activeX,
97-
y: pageY - containerPageY - activeY
98-
};
99-
100-
setDragStartValues(itemKey);
101-
},
102-
[
103-
activeHandleOffset,
104-
activeHandleMeasurements,
105-
activeItemKey,
106-
containerRef,
107-
itemPositions,
108-
itemKey,
109-
setDragStartValues,
110-
viewRef
111-
]
112-
);
113-
114-
const gestureWithMeasure = useMemo(
115-
() =>
116-
gesture.onBegin(() => {
117-
'worklet';
118-
measureHandle(false);
119-
}),
120-
[gesture, measureHandle]
121-
);
61+
const onLayout = useCallback(() => {
62+
'worklet';
63+
if (isActive.value) {
64+
updateActiveHandleMeasurements(itemKey);
65+
}
66+
}, [itemKey, isActive, updateActiveHandleMeasurements]);
12267

12368
return (
124-
<GestureDetector
125-
gesture={gestureWithMeasure.enabled(dragEnabled)}
126-
userSelect='none'>
127-
<View
128-
collapsable={false}
129-
ref={viewRef}
130-
onLayout={dragEnabled ? () => measureHandle(true) : undefined}>
69+
<GestureDetector gesture={gesture.enabled(dragEnabled)} userSelect='none'>
70+
<View collapsable={false} ref={handleRef} onLayout={onLayout}>
13171
{children}
13272
</View>
13373
</GestureDetector>

packages/react-native-sortables/src/components/shared/DraggableView/DraggableView.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ function DraggableView({
6262
activationAnimationProgress,
6363
portalState
6464
);
65-
6665
const gesture = useItemPanGesture(key, activationAnimationProgress);
6766

6867
useEffect(() => {

packages/react-native-sortables/src/providers/shared/CustomHandleProvider.ts

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import type { ReactNode } from 'react';
2-
import type { MeasuredDimensions } from 'react-native-reanimated';
3-
import { useSharedValue } from 'react-native-reanimated';
1+
import { type ReactNode, useCallback } from 'react';
2+
import type { View } from 'react-native';
3+
import type { AnimatedRef, MeasuredDimensions } from 'react-native-reanimated';
4+
import { measure, runOnUI, useSharedValue } from 'react-native-reanimated';
45

5-
import { useUIStableCallback } from '../../hooks';
66
import type { CustomHandleContextType, Vector } from '../../types';
77
import { useAnimatedDebounce } from '../../utils';
88
import { createProvider } from '../utils';
9+
import { useCommonValuesContext } from './CommonValuesProvider';
910

1011
type CustomHandleProviderProps = {
1112
children?: ReactNode;
@@ -15,32 +16,80 @@ const { CustomHandleProvider, useCustomHandleContext } = createProvider(
1516
'CustomHandle',
1617
{ guarded: false }
1718
)<CustomHandleProviderProps, CustomHandleContextType>(() => {
18-
const activeHandleOffset = useSharedValue<Vector | null>(null);
19+
const { containerRef, itemPositions } = useCommonValuesContext();
20+
const debounce = useAnimatedDebounce();
21+
22+
const fixedItemKeys = useSharedValue<Record<string, boolean>>({});
23+
const handleRefs = useSharedValue<Record<string, AnimatedRef<View>>>({});
1924
const activeHandleMeasurements = useSharedValue<MeasuredDimensions | null>(
2025
null
2126
);
22-
const fixedItemKeys = useSharedValue<Record<string, boolean>>({});
23-
const debounce = useAnimatedDebounce();
27+
const activeHandleOffset = useSharedValue<Vector | null>(null);
28+
29+
const registerHandle = useCallback(
30+
(key: string, handleRef: AnimatedRef<View>, fixed: boolean) => {
31+
runOnUI(() => {
32+
'worklet';
33+
handleRefs.value[key] = handleRef;
34+
if (fixed) {
35+
fixedItemKeys.value[key] = true;
36+
debounce(fixedItemKeys.modify, 100);
37+
}
38+
})();
2439

25-
const makeItemFixed = useUIStableCallback((key: string) => {
26-
'worklet';
27-
fixedItemKeys.value[key] = true;
28-
debounce(fixedItemKeys.modify, 100);
29-
});
40+
const unregister = () => {
41+
'worklet';
42+
delete handleRefs.value[key];
43+
if (fixed) {
44+
fixedItemKeys.value[key] = false;
45+
debounce(fixedItemKeys.modify, 100);
46+
}
47+
};
3048

31-
const removeFixedItem = useUIStableCallback((key: string) => {
32-
'worklet';
33-
delete fixedItemKeys.value[key];
34-
debounce(fixedItemKeys.modify, 100);
35-
});
49+
return runOnUI(unregister);
50+
},
51+
[debounce, fixedItemKeys, handleRefs]
52+
);
53+
54+
const updateActiveHandleMeasurements = useCallback(
55+
(key: string) => {
56+
'worklet';
57+
const handleRef = handleRefs.value[key];
58+
if (!handleRef) {
59+
return;
60+
}
61+
62+
const handleMeasurements = measure(handleRef);
63+
const containerMeasurements = measure(containerRef);
64+
const itemPosition = itemPositions.value[key];
65+
66+
if (!handleMeasurements || !containerMeasurements || !itemPosition) {
67+
return;
68+
}
69+
70+
activeHandleMeasurements.value = handleMeasurements;
71+
const { x: itemX, y: itemY } = itemPosition;
72+
activeHandleOffset.value = {
73+
x: handleMeasurements.pageX - containerMeasurements.pageX - itemX,
74+
y: handleMeasurements.pageY - containerMeasurements.pageY - itemY
75+
};
76+
},
77+
[
78+
activeHandleMeasurements,
79+
activeHandleOffset,
80+
containerRef,
81+
handleRefs,
82+
itemPositions
83+
]
84+
);
3685

3786
return {
3887
value: {
3988
activeHandleMeasurements,
4089
activeHandleOffset,
4190
fixedItemKeys,
42-
makeItemFixed,
43-
removeFixedItem
91+
registerHandle,
92+
updateActiveHandleMeasurements
4493
}
4594
};
4695
});

0 commit comments

Comments
 (0)