I'm very new to React Native (and React/Javascript in general to be honest) and am very stuck. I have a chore tracking app that renders a list of chores using Flatlist, that can then be swiped/checked off using React Native Gesture Handler Swipeable.
I want a tag above the chore list that shows the total number of chores completed (the placeholder currently says "?? Chores complete"). I know this entails finding 1) How many of my ChoreListItems are rendered from the Flatlist component and then 2) How many of those items have a state of "isComplete". I have several nested/reusable components since there will be several versions of this screen (Laundry, kitchen, bathroom, etc.) and I know that's making it even more confusing for me. I feel like there's probably an obvious answer here, but I'm lost on how to even begin unpacking this.
Here is the ChoreListItem (what is rendered from Flatlist and able to be swiped):
import React, { useState, useRef} from 'react';
import { Animated, Image, StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native';
import Swipeable from 'react-native-gesture-handler/Swipeable';
import appStyles from '../config/appStyles';
import colors from '../config/colors';
import BodyText from '../config/BodyText';
function ChoreListItem({title}) {
const [isComplete, setIsComplete] = useState(false);
const swipeableRef = useRef();
//Track interaction occurs on left swipe
const LeftActions = (progress, dragX) => {
const scale = dragX.interpolate({
inputRange: [0, 100],
outputRange: [0,1],
extrapolate: 'clamp',
});
if (isComplete == false) {
return (
<View style ={[appStyles.card, styles.leftActions]}>
<Animated.Text style={[styles.swipeText, {transform: [{scale}], fontSize:16, }]}>Swipe to track</Animated.Text>
<Image source={require('../assets/control_arrow_right.png')} style={{alignItems:'center',tintColor:colors.baseWhite,}}/>
</View>
);
}
};
//Untrack button renders on right swipe
const RightActions = (progress, dragX) => {
const scale = dragX.interpolate({
inputRange: [-100,0],
outputRange: [1,0],
extrapolate: 'clamp',
});
if (isComplete === true) {
return (
<TouchableWithoutFeedback onPress={closeSwipeable}>
<View style ={[appStyles.card, styles.rightActions]}>
<Animated.Text style={[styles.swipeText,{transform: [{scale}], fontSize:16, }]}>Tap to untrack</Animated.Text>
</View>
</TouchableWithoutFeedback>
);
}
};
//Closes swiped action and changes state
const closeSwipeable = () => {
if (isComplete===false) {
setIsComplete (true);
console.log(title + ' tracked');
} else {
setIsComplete(false);
console.log(title + ' untracked');
}
}
return (
<Swipeable
ref={swipeableRef}
state={isComplete}
renderLeftActions={LeftActions}
leftThreshold={20}
rightThreshold={10}
overshootRight={false}
renderRightActions={RightActions}
onSwipeableLeftOpen={closeSwipeable}
>
<View style={[appStyles.card, styles.choreListItem]}>
<BodyText style={{textDecorationLine: isComplete ? "line-through" : "none"}}>{title}</BodyText>
<Image style={[styles.checkmark, {display: isComplete ? "flex" : "none"}]} source={require('../assets/checkmark_large.png')}/>
</View>
</Swipeable>
);
}
export default ChoreListItem;
const styles = StyleSheet.create({
checkmark: {
width:16,
height:16,
},
choreListItem: {
paddingLeft:16,
paddingRight:16,
paddingTop:20,
paddingBottom:20,
marginBottom:16,
flex:1,
flexDirection:'row',
alignItems:'center',
justifyContent:'space-between'
},
swipeText: {
color: colors.baseWhite,
},
leftActions: {
paddingLeft:16,
paddingRight:16,
paddingTop:20,
paddingBottom:20,
marginBottom: 16,
backgroundColor: colors.primaryBlue,
flex: 1,
shadowColor: 'transparent',
alignItems:'center',
flexDirection:'row'
},
rightActions: {
paddingLeft:16,
paddingRight:16,
paddingTop:20,
paddingBottom:20,
marginBottom: 16,
backgroundColor: colors.primaryPurple,
shadowColor: 'transparent',
alignItems:'flex-end',
flexDirection:'row',
},
});
Here is the ChoreList (includes the Flatlist component):
import React from 'react';
import {FlatList, StyleSheet, Text, View} from 'react-native';
import appStyles from '../config/appStyles';
import colors from '../config/colors';
import ChoreListItem from '../components/ChoreListItem';
import SectionTitleBar from '../components/SectionTitleBar';
function ChoreList({getCategory}) {
return (
<View style={appStyles.containerPadding}>
{/* <SectionTitleBar title="Today"/> */}
<View>
<FlatList
data = {getCategory}
renderItem={({item}) =>
<ChoreListItem title={item.title} />
}
keyExtractor={(item) => item.title.toString()}
/>
</View>
</View>
);
}
export default ChoreList;
const styles = StyleSheet.create({
choreListItem: {
padding:16,
marginBottom:16,
},
});
Here is the component/screen with all of the props:
import React from 'react';
import { Text, View } from 'react-native';
import choreCats from '../config/choreCats';
import choreItems from '../config/choreItems';
import colors from '../config/colors';
import ChoreList from '../components/ChoreList';
import PageHeader_TitleIllo from '../components/pageHeaders/PageHeader_TitleIllo';
import Screen from '../components/Screen';
function LaundryChoresScreen() {
const choreCategory = choreCats[4];
return (
<Screen>
<PageHeader_TitleIllo
category={choreCategory.category}
image={choreCategory.image}
bgColor={choreCategory.bgColor}
/>
<Text style={{padding:8, backgroundColor:colors.blueHueMedium, alignSelf:'flex-start', marginTop: 8, marginBottom: 8}}>?? Chores complete</Text>
<View style={{backgroundColor:colors.baseFog,}}>
<ChoreList getCategory={choreItems.filter(({category})=>category=== "laundry")}/>
</View>
</Screen>
);
}
export default LaundryChoresScreen;