Skip to content

Commit 5ff360c

Browse files
authored
fix: Items not animated before sortEnabled is set to true (#398)
## Description This issue was caused by the optimization that delayed transition to absolute layout until `sortEnabled` is set to `true`. I remove this optimization as it shouldn't affect performance that much but added unnecessary complexity and introduced this issue.
1 parent 3262064 commit 5ff360c

File tree

9 files changed

+35
-113
lines changed

9 files changed

+35
-113
lines changed

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

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import type {
2222
DropIndicatorSettings,
2323
Overflow
2424
} from '../../types';
25-
import { AbsoluteLayoutState } from '../../types';
2625
import AnimatedOnLayoutView from './AnimatedOnLayoutView';
2726
import DropIndicator from './DropIndicator';
2827

@@ -48,14 +47,14 @@ export default function SortableContainer({
4847
style
4948
}: AnimatedHeightContainerProps) {
5049
const {
51-
absoluteLayoutState,
5250
activeItemDropped,
5351
activeItemKey,
5452
containerHeight,
5553
containerRef,
5654
containerWidth,
5755
controlledContainerDimensions,
58-
shouldAnimateLayout
56+
shouldAnimateLayout,
57+
usesAbsoluteLayout
5958
} = useCommonValuesContext();
6059
const { handleHelperContainerMeasurement, measurementsContainerRef } =
6160
useMeasurementsContext();
@@ -64,7 +63,7 @@ export default function SortableContainer({
6463
const animateLayout = dimensionsAnimationType === 'layout';
6564

6665
const outerContainerStyle = useAnimatedStyle(() => {
67-
if (absoluteLayoutState.value !== AbsoluteLayoutState.COMPLETE) {
66+
if (!usesAbsoluteLayout.value) {
6867
return EMPTY_OBJECT;
6968
}
7069

@@ -92,7 +91,7 @@ export default function SortableContainer({
9291
}, [dimensionsAnimationType]);
9392

9493
const innerContainerStyle = useAnimatedStyle(() => {
95-
if (absoluteLayoutState.value !== AbsoluteLayoutState.COMPLETE) {
94+
if (!usesAbsoluteLayout.value) {
9695
return EMPTY_OBJECT;
9796
}
9897

@@ -110,16 +109,15 @@ export default function SortableContainer({
110109
});
111110

112111
const animatedMeasurementsContainerStyle = useAnimatedStyle(() => {
113-
if (absoluteLayoutState.value === AbsoluteLayoutState.PENDING) {
112+
const ctrl = controlledContainerDimensions.value;
113+
const height = ctrl.height ? containerHeight.value : undefined;
114+
const width = ctrl.width ? containerWidth.value : undefined;
115+
116+
if (!height && !width) {
114117
return EMPTY_OBJECT;
115118
}
116119

117-
const ctrl = controlledContainerDimensions.value;
118-
119-
return {
120-
height: ctrl.height ? containerHeight.value : undefined,
121-
width: ctrl.width ? containerWidth.value : undefined
122-
};
120+
return { height, width };
123121
});
124122

125123
return (

packages/react-native-sortables/src/providers/layout/flex/FlexLayoutProvider.tsx

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import {
99
import { type DEFAULT_SORTABLE_FLEX_PROPS, IS_WEB } from '../../../constants';
1010
import { useDebugContext } from '../../../debug';
1111
import {
12-
AbsoluteLayoutState,
1312
type FlexLayout,
1413
type FlexLayoutContextType,
1514
type RequiredBy,
@@ -56,7 +55,6 @@ const { FlexLayoutProvider, useFlexLayoutContext } = createProvider(
5655
width
5756
}) => {
5857
const {
59-
absoluteLayoutState,
6058
controlledContainerDimensions,
6159
indexToKey,
6260
itemDimensions,
@@ -147,10 +145,6 @@ const { FlexLayoutProvider, useFlexLayoutContext } = createProvider(
147145
paddings: paddings.value
148146
},
149147
(props, previousProps) => {
150-
if (absoluteLayoutState.value === AbsoluteLayoutState.PENDING) {
151-
return;
152-
}
153-
154148
onChange(
155149
props && calculateLayout(props),
156150
// On web, animate layout only if parent container is not resized
@@ -171,8 +165,7 @@ const { FlexLayoutProvider, useFlexLayoutContext } = createProvider(
171165
rowGap,
172166
itemDimensions,
173167
dimensionsLimits,
174-
paddings,
175-
absoluteLayoutState
168+
paddings
176169
]
177170
);
178171

packages/react-native-sortables/src/providers/layout/grid/GridLayoutProvider.tsx

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { IS_WEB } from '../../../constants';
1010
import { useDebugContext } from '../../../debug';
1111
import { useAnimatableValue } from '../../../hooks';
1212
import {
13-
AbsoluteLayoutState,
1413
type Animatable,
1514
type GridLayout,
1615
type GridLayoutContextType
@@ -44,7 +43,6 @@ const { GridLayoutProvider, useGridLayoutContext } = createProvider(
4443
rowHeight
4544
}) => {
4645
const {
47-
absoluteLayoutState,
4846
indexToKey,
4947
itemDimensions,
5048
itemPositions,
@@ -128,10 +126,6 @@ const { GridLayoutProvider, useGridLayoutContext } = createProvider(
128126
numGroups
129127
}),
130128
(props, previousProps) => {
131-
if (absoluteLayoutState.value === AbsoluteLayoutState.PENDING) {
132-
return;
133-
}
134-
135129
onChange(
136130
calculateLayout(props),
137131
// On web, animate layout only if parent container is not resized
@@ -142,15 +136,7 @@ const { GridLayoutProvider, useGridLayoutContext } = createProvider(
142136
);
143137
}
144138
),
145-
[
146-
mainGroupSize,
147-
mainGap,
148-
crossGap,
149-
numGroups,
150-
isVertical,
151-
itemDimensions,
152-
absoluteLayoutState
153-
]
139+
[mainGroupSize, mainGap, crossGap, numGroups, isVertical, itemDimensions]
154140
);
155141

156142
const useGridLayout = useCallback(

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import type {
2121
Maybe,
2222
Vector
2323
} from '../../types';
24-
import { AbsoluteLayoutState } from '../../types';
2524
import { areArraysDifferent, getKeyToIndex } from '../../utils';
2625
import { createProvider } from '../utils';
2726
import { useActiveItemValuesContext } from './ActiveItemValuesProvider';
@@ -117,7 +116,7 @@ const { CommonValuesContext, CommonValuesProvider, useCommonValuesContext } =
117116
// OTHER
118117
const containerRef = useAnimatedRef<View>();
119118
const sortEnabled = useAnimatableValue(_sortEnabled);
120-
const absoluteLayoutState = useSharedValue(AbsoluteLayoutState.PENDING);
119+
const usesAbsoluteLayout = useSharedValue(false);
121120
const shouldAnimateLayout = useSharedValue(true);
122121
const animateLayoutOnReorderOnly = useDerivedValue(
123122
() => itemsLayoutTransitionMode === 'reorder',
@@ -138,7 +137,6 @@ const { CommonValuesContext, CommonValuesProvider, useCommonValuesContext } =
138137
return {
139138
value: {
140139
...useActiveItemValuesContext(),
141-
absoluteLayoutState,
142140
activationAnimationDuration,
143141
activeItemOpacity,
144142
activeItemScale,
@@ -168,7 +166,8 @@ const { CommonValuesContext, CommonValuesProvider, useCommonValuesContext } =
168166
shouldAnimateLayout,
169167
snapOffsetX,
170168
snapOffsetY,
171-
sortEnabled
169+
sortEnabled,
170+
usesAbsoluteLayout
172171
}
173172
};
174173
});

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

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,7 @@ import type {
2121
SortableCallbacks,
2222
Vector
2323
} from '../../types';
24-
import {
25-
AbsoluteLayoutState,
26-
DragActivationState,
27-
LayerState
28-
} from '../../types';
24+
import { DragActivationState, LayerState } from '../../types';
2925
import {
3026
clearAnimatedTimeout,
3127
getKeyToIndex,
@@ -60,7 +56,6 @@ const { DragProvider, useDragContext } = createProvider('Drag')<
6056
overDrag
6157
}) => {
6258
const {
63-
absoluteLayoutState,
6459
activationAnimationDuration,
6560
activationState,
6661
activeAnimationProgress,
@@ -86,7 +81,8 @@ const { DragProvider, useDragContext } = createProvider('Drag')<
8681
snapOffsetX,
8782
snapOffsetY,
8883
sortEnabled,
89-
touchPosition
84+
touchPosition,
85+
usesAbsoluteLayout
9086
} = useCommonValuesContext();
9187
const { measureContainer } = useMeasurementsContext();
9288
const { updateLayer } = useLayerContext() ?? {};
@@ -351,7 +347,7 @@ const { DragProvider, useDragContext } = createProvider('Drag')<
351347
return;
352348
}
353349

354-
if (absoluteLayoutState.value !== AbsoluteLayoutState.COMPLETE) {
350+
if (!usesAbsoluteLayout.value) {
355351
measureContainer();
356352
}
357353

@@ -364,7 +360,7 @@ const { DragProvider, useDragContext } = createProvider('Drag')<
364360
// Start handling touch after a delay to prevent accidental activation
365361
// e.g. while scrolling the ScrollView
366362
activationTimeoutId.value = setAnimatedTimeout(() => {
367-
if (absoluteLayoutState.value !== AbsoluteLayoutState.COMPLETE) {
363+
if (!usesAbsoluteLayout.value) {
368364
return;
369365
}
370366

@@ -386,7 +382,7 @@ const { DragProvider, useDragContext } = createProvider('Drag')<
386382
}, dragActivationDelay.value);
387383
},
388384
[
389-
absoluteLayoutState,
385+
usesAbsoluteLayout,
390386
activeItemKey,
391387
activationState,
392388
activationTimeoutId,

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

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,7 @@ import {
1010

1111
import { OFFSET_EPS } from '../../constants';
1212
import { useUIStableCallback } from '../../hooks';
13-
import {
14-
AbsoluteLayoutState,
15-
type Dimensions,
16-
type MeasurementsContextType
17-
} from '../../types';
13+
import { type Dimensions, type MeasurementsContextType } from '../../types';
1814
import { areDimensionsDifferent, useAnimatedDebounce } from '../../utils';
1915
import { createProvider } from '../utils';
2016
import { useCommonValuesContext } from './CommonValuesProvider';
@@ -27,7 +23,6 @@ const { MeasurementsProvider, useMeasurementsContext } = createProvider(
2723
'Measurements'
2824
)<MeasurementsProviderProps, MeasurementsContextType>(({ itemsCount }) => {
2925
const {
30-
absoluteLayoutState,
3126
activeItemDimensions,
3227
activeItemKey,
3328
containerHeight,
@@ -36,7 +31,7 @@ const { MeasurementsProvider, useMeasurementsContext } = createProvider(
3631
itemDimensions,
3732
measuredContainerHeight,
3833
measuredContainerWidth,
39-
sortEnabled
34+
usesAbsoluteLayout
4035
} = useCommonValuesContext();
4136

4237
const measurementsContainerRef = useAnimatedRef<View>();
@@ -68,17 +63,12 @@ const { MeasurementsProvider, useMeasurementsContext } = createProvider(
6863
// Update the array of item dimensions only after all items have been
6964
// measured to reduce the number of times animated reactions are triggered
7065
if (measuredItemsCount.value === itemsCount) {
71-
// Don't update dimensions if the sortable component is first rendered
72-
// and the layout cannot be changed to absolute (e.g. because sorting
73-
// hasn't been enabled yet)
74-
const canUpdateDimensions =
75-
absoluteLayoutState.value !== AbsoluteLayoutState.PENDING;
7666
// If this is the first time all items have been measured, update
7767
// dimensions immediately to avoid unnecessary delays
7868
if (!initialItemMeasurementsCompleted.value) {
7969
initialItemMeasurementsCompleted.value = true;
80-
if (canUpdateDimensions) itemDimensions.modify();
81-
} else if (canUpdateDimensions) {
70+
itemDimensions.modify();
71+
} else if (usesAbsoluteLayout.value) {
8272
// In all other cases, debounce the update in case multiple items
8373
// change their size at the same time
8474
debounce(itemDimensions.modify, 100);
@@ -130,7 +120,7 @@ const { MeasurementsProvider, useMeasurementsContext } = createProvider(
130120
measuredContainerHeight.value = dimensions.height;
131121
measuredContainerWidth.value = dimensions.width;
132122

133-
if (absoluteLayoutState.value === AbsoluteLayoutState.COMPLETE) {
123+
if (usesAbsoluteLayout.value) {
134124
if (!controlledContainerDimensions.value.height) {
135125
containerHeight.value = dimensions.height;
136126
}
@@ -140,7 +130,7 @@ const { MeasurementsProvider, useMeasurementsContext } = createProvider(
140130
}
141131
},
142132
[
143-
absoluteLayoutState,
133+
usesAbsoluteLayout,
144134
containerHeight,
145135
containerWidth,
146136
controlledContainerDimensions,
@@ -164,21 +154,6 @@ const { MeasurementsProvider, useMeasurementsContext } = createProvider(
164154
}
165155
}, [applyMeasuredContainerDimensions, measurementsContainerRef]);
166156

167-
useAnimatedReaction(
168-
() => sortEnabled.value,
169-
enabled => {
170-
if (
171-
absoluteLayoutState.value === AbsoluteLayoutState.PENDING &&
172-
enabled
173-
) {
174-
// Transition from the relative (Pending) to the Absolute (Complete)
175-
// layout when sorting is enabled for the first time
176-
absoluteLayoutState.value = AbsoluteLayoutState.TRANSITION;
177-
itemDimensions.modify();
178-
}
179-
}
180-
);
181-
182157
useAnimatedReaction(
183158
() => ({
184159
containerH: containerHeight.value,
@@ -195,8 +170,7 @@ const { MeasurementsProvider, useMeasurementsContext } = createProvider(
195170
measuredWidth
196171
}) => {
197172
if (
198-
// Update only if absolute layout is during the transition state
199-
absoluteLayoutState.value !== AbsoluteLayoutState.TRANSITION ||
173+
usesAbsoluteLayout.value ||
200174
!itemMeasurementsCompleted ||
201175
measuredHeight === null ||
202176
measuredWidth === null ||
@@ -209,7 +183,7 @@ const { MeasurementsProvider, useMeasurementsContext } = createProvider(
209183
return;
210184
}
211185

212-
absoluteLayoutState.value = AbsoluteLayoutState.COMPLETE;
186+
usesAbsoluteLayout.value = true;
213187
}
214188
);
215189

packages/react-native-sortables/src/providers/shared/hooks/useItemLayoutStyles.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
withTiming
88
} from 'react-native-reanimated';
99

10-
import { AbsoluteLayoutState, type AnimatedStyleProp } from '../../../types';
10+
import { type AnimatedStyleProp } from '../../../types';
1111
import { useCommonValuesContext } from '../CommonValuesProvider';
1212
import useItemZIndex from './useItemZIndex';
1313

@@ -30,14 +30,14 @@ export default function useItemLayoutStyles(
3030
activationAnimationProgress: SharedValue<number>
3131
): AnimatedStyleProp {
3232
const {
33-
absoluteLayoutState,
3433
activeItemDropped,
3534
activeItemKey,
3635
activeItemPosition,
3736
animateLayoutOnReorderOnly,
3837
dropAnimationDuration,
3938
itemPositions,
40-
shouldAnimateLayout
39+
shouldAnimateLayout,
40+
usesAbsoluteLayout
4141
} = useCommonValuesContext();
4242

4343
const zIndex = useItemZIndex(key, activationAnimationProgress);
@@ -94,7 +94,7 @@ export default function useItemLayoutStyles(
9494
);
9595

9696
return useAnimatedStyle(() => {
97-
if (absoluteLayoutState.value !== AbsoluteLayoutState.COMPLETE) {
97+
if (!usesAbsoluteLayout.value) {
9898
return RELATIVE_STYLE;
9999
}
100100

0 commit comments

Comments
 (0)