I have two list components or card stacks. 5 Decks and several cards in each deck. I am listing the 5 decks and passing it’s content to a child component when I click on it with a (selectedItem) state. Then I’m picking that up in its child component and mapping another flatlist with the cards inside.
My problem is, when I click to an index that is beyond the limit of a smaller deck. FOr example,
Deck1 = 23 cards
Deck2 = 15 cards
When I index+1 on Deck1 to anywhere past 15, and I try to click on Deck2, it says it’s out of range (obviously lol), I’d like it to reset to index(0) when I change decks. I can’t just add more cards and make them all the same amount because I am setting a new feature later where the user can build his own deck and it can be any number of cards
Here’s my Decks Component
const Decks = () => {
const {theme} = useContext(ThemeContext)
let activeColors = colors[theme.mode]
const allbelts = [yellowbelt, orangebelt, greenbelt, brownbeltthree, brownbelttwo, brownbeltone]
const ref = useRef(null);
const [selectedItem, setSelectedItem] = useState(null);
const [deckIndex, setDeckIndex] = useState(0);
const handleItemPress = (item) => {
setSelectedItem(item);
console.log("deckIndex:", deckIndex);
};
return (
<View style={{flex:1}}>
<View style={{flex:1, marginTop: 40}}>
<Text style={[{color:activeColors.primary}, styles.sectionheading]}>
Testing Card Decks
</Text>
<Animated.FlatList
ref={ref}
data={allbelts}
horizontal
keyExtractor={(item, index) => index.toString()}
showsHorizontalScrollIndicator={false}
snapToInterval={ITEM_SIZE}
decelerationRate={0}
bounces={false}
scrollEventThrottle={16}
renderItem={({item, deckIndex}) => (
<View
style={{}}>
<TouchableOpacity
key={deckIndex}
onPress={() => {
handleItemPress(item)
}}>
<Animated.View
style={[{
backgroundColor: item.color,
shadowColor: item.color,
borderColor: activeColors.bgalt},
styles.card]}>
<Text
numberOfLines={2}
style={[{color: item.color === "#F5D143" ? activeColors.black : activeColors.white}, styles.cardtitle]}>
{item.rank_japanese}
</Text>
<Text
numberOfLines={2}
style={[{color: item.color === "#F5D143" ? activeColors.black : activeColors.white}, styles.cardtitle]}>
{item.rank_english}
</Text>
</Animated.View>
</TouchableOpacity>
</View>
)}
/>
<TestingCards selectedItem={selectedItem} deckIndex={deckIndex} />
</View>
</View>
)
}
export default Decks
and here is the Cards component
const Card = ({info}) => {
const {theme} = useContext(ThemeContext)
let activeColors = colors[theme.mode]
const rotate = useSharedValue(0);
const frontAnimatedStyle = useAnimatedStyle(() => {
const rotateValue = interpolate(
rotate.value,
[0, 1],
[0, 180]
);
return {
transform: [
{rotateY: withTiming(`${rotateValue}deg`, {duration: 500})}
]
}
})
const backAnimatedStyle = useAnimatedStyle(() => {
const rotateValue = interpolate(
rotate.value,
[0, 1],
[180, 360]
);
return {
transform: [
{rotateY: withTiming(`${rotateValue}deg`, {duration: 500})}
]
}
})
return (
<View style={[styles.cardcontainer]}>
<View style={styles.iconcnt}>
<Pressable
onPress={() => {
rotate.value = rotate.value ? 0 : 1;
}}
style={styles.icon}>
<Icon name="swap-horizontal" size={24} color={activeColors.textcolor} />
</Pressable>
</View>
<View style={styles.cardContent}>
<Animated.View style={[ {backgroundColor: activeColors.bgalt, position:"absolute"}, styles.card, frontAnimatedStyle]}>
<View>
<Text
style={[{color: activeColors.textcolor}, styles.cardtext]}>
{info.q}
</Text>
</View>
</Animated.View>
<Animated.View style={[ {backgroundColor: activeColors.primary}, styles.card, backAnimatedStyle]}>
<View>
<Text
style={[{color: activeColors.textcolor}, styles.cardtext]}>
{info.a}
</Text>
</View>
</Animated.View>
</View>
</View>
)
}
const FlashCards = ({selectedItem, deckIndex}) => {
const {theme} = useContext(ThemeContext)
let activeColors = colors[theme.mode]
const [index, setIndex] = useState(0);
const [lastIndex, setLastIndex] = useState(null);
const [viewPosition, setViewPosition] = useState(0.5);
const flatlistRef = useRef(null)
useEffect(() => {
setLastIndex(selectedItem?.questions?.length - 1);
// console.log("selectedItem:", selectedItem);
console.log("lastIndex at TestingCards:", lastIndex);
flatlistRef.current?.scrollToIndex({
index: index,
animated: true,
viewPosition,
})
} , [index, viewPosition, lastIndex, selectedItem])
if (!selectedItem) {
return null;
}
return (
<View style={[styles.container]}>
<Animated.FlatList
data={selectedItem.questions}
ref={flatlistRef}
index={deckIndex}
keyExtractor={(item, index) => index.toString()}
initialScrollIndex={0}
horizontal
showsHorizontalScrollIndicator={false}
snapToInterval="ITEM_SIZE"
decelerationRate={0}
bounces={false}
scrollEventThrottle={16}
initialNumToRender={10}
onScrollToIndexFailed={index => {
setIndex(0)
const wait = new Promise(resolve => setTimeout(resolve, 0));
wait.then(() => {
flatlistRef.current?.scrollToIndex({ index: index, animated: true });
});
}}
renderItem={({item, index}) => <Card info={item} index={index} totalLength={lastIndex} seefront={true} />}
/>
<View style={styles.btnGroup}>
<TouchableOpacity
onPress={()=>{
setIndex(0);
setViewPosition(0.5);
}}>
<LinearGradient
colors={['rgba(1,102,175,1)', 'rgba(155,24,213,1)']}
start={{ x: 0, y: 1 }}
end={{ x: 1, y: 1 }}
style={styles.btnPrimary}>
<Icon name="chevron-double-left" size={24} color={activeColors.white} />
</LinearGradient>
</TouchableOpacity>
<TouchableOpacity
onPress={()=>{
if (index === 0) {
return;
}
setIndex(index - 1);
setViewPosition(0.5);
}}>
<LinearGradient
colors={['rgba(1,102,175,1)', 'rgba(155,24,213,1)']}
start={{ x: 0, y: 1 }}
end={{ x: 1, y: 1 }}
style={styles.btnPrimary}>
<Icon name="chevron-left" size={24} color={activeColors.white} />
</LinearGradient>
</TouchableOpacity>
<TouchableOpacity
onPress={()=>{
if (index === lastIndex) {
return;
}
setIndex(index + 1);
setViewPosition(0.5);
}}>
<LinearGradient
colors={['rgba(1,102,175,1)', 'rgba(155,24,213,1)']}
start={{ x: 0, y: 1 }}
end={{ x: 1, y: 1 }}
style={styles.btnPrimary}>
<Icon name="chevron-right" size={24} color={activeColors.white} />
</LinearGradient>
</TouchableOpacity>
<TouchableOpacity
onPress={()=>{
if (index === lastIndex) {
return;
}
setIndex(lastIndex - 1);
setViewPosition(0.5);
}}>
<LinearGradient
colors={['rgba(1,102,175,1)', 'rgba(155,24,213,1)']}
start={{ x: 0, y: 1 }}
end={{ x: 1, y: 1 }}
style={styles.btnPrimary}>
<Icon name="chevron-double-right" size={24} color={activeColors.white} />
</LinearGradient>
</TouchableOpacity>
</View>
</View>
)
}
export default FlashCards