Here is the code I am using to initialize my shared values:
export default function CardList({ children, players, setPlayers, playerTurnCounter }) {
const [ready, setReady] = useState(false);
const offsets = useSharedValue(children.map(() => ({
order: 0,
width: 0,
height: 0,
x: 0,
y: 0,
originalX: 0,
originalY: 0,
}));
if (!ready) {
return (
<View style={styles.row}>
{children.map((child, index) => {
return (
<View
key={child.key}
onLayout={({
nativeEvent: { layout: { x, y, width, height } },
}) => {
offsets.value[index] = {
order: -1,
width: width / children + 10,
height,
x,
y,
originalX: x,
originalY: y,
);
runOnUI(() => {
'worklet';
if (offsets.filter((o) => o.order.value !== -1).length === 0) {
runOnJS(setReady)(true);
}
})();
}}>
{child}
</View>
);
})}
</View>
);
}
return (
<View style={styles.container}>
{children.map((child, index) => (
<SortableCard
key={child.key}
offsets={offsets}
index={index}
players={players}
setPlayers={setPlayers}
containerWidth={containerWidth}
playerTurnCounter={playerTurnCounter}>
{child}
</SortableCard>
))}
</View>
);
}
And here is my useAnimatedGestureHandler():
/* eslint-disable react/prop-types */
import React, { ReactElement } from 'react';
import { StyleSheet } from 'react-native';
import { PanGestureHandler, PanGestureHandlerGestureEvent } from 'react-native-gesture-handler';
import Animated, {
runOnJS,
useAnimatedGestureHandler,
useAnimatedStyle,
useDerivedValue,
useSharedValue,
withSpring,
} from 'react-native-reanimated';
import { between, useVector } from 'react-native-redash';
import {
calculateLayout,
CARD_HEIGHT,
lastOrder,
MARGIN_LEFT,
MARGIN_TOP,
remove,
reorder,
SENTENCE_HEIGHT,
} from '../Layout';
function SortableCard({ offsets, index, children, players, setPlayers, containerWidth }) {
const offset = offsets.value[index];
const isGestureActive = useSharedValue(false);
const isAnimating = useSharedValue(false);
const translation = useVector();
const isInBank = useDerivedValue(() => offset.order === -1);
const onGestureEvent = useAnimatedGestureHandler({
onStart: (_, ctx) => {
if (isInBank.value) {
translation.x.value = offset.originalX - MARGIN_LEFT;
translation.y.value = offset.originalY + MARGIN_TOP;
} else {
translation.x.value = offset.x;
translation.y.value = offset.y;
}
ctx.x = translation.x.value;
ctx.y = translation.y.value;
isGestureActive.value = true;
},
onActive: ({ translationX, translationY }, ctx) => {
translation.x.value = ctx.x + translationX;
translation.y.value = ctx.y + translationY;
if (isInBank.value && translation.y.value < SENTENCE_HEIGHT) {
offset.order = lastOrder(offsets.value);
calculateLayout(offsets.value, containerWidth);
} else if (!isInBank.value && translation.y.value > SENTENCE_HEIGHT) {
offset.order = -1;
remove(offsets.value, index);
calculateLayout(offsets.value, containerWidth);
}
for (let i = 0; i < offsets.value.length; i++) {
const o = offsets.value[i];
if (i === index && o.order !== -1) {
continue;
}
if (
between(translation.x.value, o.x, o.x + o.width) &&
between(translation.y.value, o.y - CARD_HEIGHT, o.y + CARD_HEIGHT)
) {
reorder(offsets.value, offset.order, o.order);
calculateLayout(offsets.value, containerWidth);
break;
}
}
},
onEnd: ({ velocityX, velocityY }) => {
isAnimating.value = true;
translation.x.value = withSpring(offset.x, { velocity: 0.2 });
translation.y.value = withSpring(offset.y, { velocity: 0.2 });
isGestureActive.value = false;
const tplayers = players;
if (
between(translation.x.value, offset.x / 2, offset.x / 2 + offset.width) &&
between(translation.y.value, offset.y - CARD_HEIGHT, offset.y)
) {
let inDeck = false;
for (let i = 0; i < tplayers[0].subHand.length; i++) {
if (
tplayers[0].subHand[i].suite === children.props.item.suite &&
tplayers[0].subHand[i].value === children.props.item.value
) {
inDeck = true;
}
}
if (!inDeck) {
tplayers[0].subHand.push(children.props.item);
}
runOnJS(setPlayers)(tplayers);
} else if (
between(translation.x.value, offset.x, offset.x + offset.width) &&
between(translation.y.value, offset.y - CARD_HEIGHT, offset.y)
) {
let inDeck = false;
for (let i = 0; i < tplayers[0].subHand.length; i++) {
if (
tplayers[0].subHand[i].suite === children.props.item.suite &&
tplayers[0].subHand[i].value === children.props.item.value
) {
inDeck = true;
}
}
if (!inDeck) {
tplayers[0].subHand.push(children.props.item);
}
runOnJS(setPlayers)(tplayers);
} else {
for (let i = 0; i < tplayers[0].subHand.length; i++) {
if (
tplayers[0].subHand[i].suite === children.props.item.suite &&
tplayers[0].subHand[i].value === children.props.item.value
) {
tplayers[0].subHand.splice(children.props.item, 1);
}
}
runOnJS(setPlayers)(tplayers);
}
},
});
const translateX = useDerivedValue(() => {
if (isGestureActive.value) {
return translation.x.value;
}
return withSpring(isInBank.value ? offset.originalX - MARGIN_LEFT : offset.x);
});
const translateY = useDerivedValue(() => {
if (isGestureActive.value) {
return translation.y.value;
}
return withSpring(isInBank.value ? offset.originalY + MARGIN_TOP : offset.y);
});
const style = useAnimatedStyle(() => {
return {
position: 'absolute',
top: 0,
left: 0,
width: offset.width.value,
height: CARD_HEIGHT,
transform: [{ translateX: translateX.value }, { translateY: translateY.value }],
};
});
return (
<>
<Animated.View style={style}>
<PanGestureHandler onGestureEvent={onGestureEvent}>
<Animated.View style={StyleSheet.absoluteFill}>{children}</Animated.View>
</PanGestureHandler>
</Animated.View>
</>
);
}
export default SortableCard;
My issue is when I try to access offsets, which is one shared value that stores an array, while inside of useAnimatedGestureHandler, the values are not as expected.
When I put print statements outside of useAnimatedGestureHandler (for example, on the line below ‘offset = offsets.value[index]’) the expected values are printed. Similarly if I console log offsets in the return statement I also see the expected values. But if I try to access offsets inside the gesture handler or if I try to print them, all values are reinitialized to their original values but the initializer doesn’t seem to get called again.
Any help to assist me in understanding what is going on here is greatly appreciated. Thank you!