I'm running into an odd problem with react native using react native gestures and reanimated 2.0. I am currently trying to make a simple to-do list that implements a 'swipe to delete' on tasks themselves. Everything works well up until I delete a task by filtering the task from the state. For some reason this causes the animation of one task to be passed onto the next that now occupies the index of the previous one that was deleted. For testing purposes, I am reducing the height to 20 and the translateX of the task less than it should be.
TestScreen.js
import { ScrollView, StyleSheet, Text, View } from "react-native";
import React, { useState } from "react";
import TestTask from "../components/TestTask";
import { useSharedValue } from "react-native-reanimated";
const names = [
{ id: 0, name: "first ting" },
{ id: 1, name: "second ting" },
{ id: 2, name: "third ting" },
{ id: 3, name: "fourth ting" },
{ id: 4, name: "fifth ting" },
];
const TestScreen = () => {
const [tasks, setTasks] = useState(
names.map((task) => {
return {
...task,
};
})
);
const deleteTask = (id) => {
setTasks((tasks) => tasks.filter((task) => task.id !== id));
};
return (
<View>
<ScrollView>
{tasks.map((task, index) => (
<TestTask key={index} task={task} deleteTask={deleteTask} />
))}
</ScrollView>
</View>
);
};
export default TestScreen;
const styles = StyleSheet.create({});
TestTask.js
import { Dimensions, StyleSheet, Text, View } from "react-native";
import React, { useEffect } from "react";
import Animated, {
runOnJS,
useAnimatedStyle,
useSharedValue,
withTiming,
} from "react-native-reanimated";
import { Gesture, GestureDetector } from "react-native-gesture-handler";
const { width: screenWidth } = Dimensions.get("window");
const deleteX = -screenWidth * 0.3;
const TestTask = ({ task, deleteTask }) => {
const height = useSharedValue(50);
const translateX = useSharedValue(0);
const animStyles = useAnimatedStyle(() => {
return {
height: height.value,
transform: [{ translateX: translateX.value }],
};
});
useEffect(() => {
return () => {
height.value = 50;
translateX.value = 0;
};
}, []);
const pan = Gesture.Pan()
.onUpdate((e) => {
translateX.value = e.translationX;
})
.onEnd(() => {
if (translateX.value < deleteX) {
translateX.value = withTiming(-100);
height.value = withTiming(20, undefined, (finished) => {
if (finished) {
runOnJS(deleteTask)(task.id);
}
});
} else {
translateX.value = withTiming(0);
}
});
return (
<GestureDetector gesture={pan}>
<Animated.View style={[styles.container, animStyles]}>
<Text>{task.name}</Text>
</Animated.View>
</GestureDetector>
);
};
export default TestTask;
const styles = StyleSheet.create({
container: {
width: "100%",
backgroundColor: "red",
borderColor: "black",
borderWidth: 1,
},
});