How do I display items in a nested array in react native? - javascript

I am working on a project in react native and how the program should work is that the user will start at RestaurantScreen.js and then once they pick a restaurant they will move to the next screen, MenuScreen.js. I want each restaurant to display the specific meal inside the nested array productData array in data.js. Right now the RestaurantScreen.js displays all the restaurants from the array restaurantData inside data.js but once you click on a restaurant it displays all the meals from each restaurant, but I just want the one meal, price, and image. Let me know if I need to provide move info.
data.js
export const restaurantData = [
{restaurantName:"Milan Mondayz",
businessAddress:"The Steak House of Hammond",
images:require('../images/milanpic.jpg'),
productData:[{
meal:"BBQ Chicken Plate with Sides",
foodImages:require('../images/Food1.jpg'),
price:12.00,
checked:false,id:"0"}],
id:"0"},
{restaurantName:"Nick's Cajun Flavors",
businessAddress:"123 Hammond Road",
images:require('../images/cajun.jpeg'),
productData:[{
meal:"Crawfish Etouffee",
price:13.99,
foodImages:require('../images/crawfishEto.jpeg'),
checked:false}],
id:1},
];
RestaurantScreen.js
export default function RestaurantScreen({ navigation }) {
const [indexCheck, setIndexCheck] = useState("0")
const { navigate } = useNavigation();
return (
<View style={styles.container}>
<View style={styles.cardView}>
{/*<Button
title="Go to Menu"
onPress ={()=>{navigation.navigate("Menu",{restaurantData})}}
/>*/}
<View style ={styles.headerTextView}>
<Text style ={styles.headerText}>Where Do You Want To Eat?</Text>
</View>
<View>
<FlatList
style={{marginTop:10, marginBottom:10}}
data={restaurantData}
vertical ={true}
keyExtractor={(restaurantData => restaurantData.id)}
extraData={indexCheck}
renderItem={({ item, index }) => (
<RestaurantCard
screenWidth={SCREEN_WIDTH*.94}
images={item.images}
restaurantName={item.restaurantName}
businessAddress={item.businessAddress}
//productData={item.productData}
OnPressRestaurantCard ={()=>{navigation.navigate("Menu",{index});
}}
/>
)}
showsVerticalScrollIndicator={false}
/>
</View>
</View>
</View>
)
}
MenuScreen.js
export default class MenuScreen extends Component {
render() {
const index = this.props.route.params.index
//const {meal,price,foodImages} = restaurantData[index]
return (
<View style={styles.container}>
<View style={styles.cardView}>
<View style ={styles.headerTextView}>
<Text style ={styles.headerText}>What Do You Want To Eat From?</Text>
</View>
<TouchableOpacity onPress={() => {this.props.navigation.navigate('Cart')}}>
<View style ={styles.cartContainer}>
<View style ={styles.cartCounterContainer}>
<Text style ={styles.cartText}>Cart</Text>
<View style ={styles.cartCounter}>
<Text style ={styles.cartNum}>0</Text>
</View>
</View>
</View>
</TouchableOpacity>
<View>
<View style ={{flex:1}}>
<View style ={styles.menuCard}>
<FlatList
style={{marginTop:10, marginBottom:10}}
data = {restaurantData}
keyExtractor= {item =>item.id}
renderItem = {({item,index})=>(
<MenuCard
screenWidth={SCREEN_WIDTH*.9}
images={item.foodImages}
meal ={item.meal}
price ={item.price}
onPressMenuCard={()=>{this.props.navigation.navigate("Payment")}}
/>
)}
/>
</View>
</View>
</View>
</View>
</View>
)
}
}

I can do it with SectionList and this is my example: https://snack.expo.dev/#pqv2210/exsectionlist
Just change name of field in your restaurantData: productData to data
export const restaurantData = [
{
restaurantName: 'Milan Mondayz',
businessAddress: 'The Steak House of Hammond',
images: require('../images/milanpic.jpg'),
data: [
{
meal: 'BBQ Chicken Plate with Sides',
foodImages: require('../images/Food1.jpg'),
price: 12.0,
checked: false,
id: '0',
},
],
id: '0',
},
{
restaurantName: "Nick's Cajun Flavors",
businessAddress: '123 Hammond Road',
images: require('../images/cajun.jpeg'),
data: [
{
meal: 'Crawfish Etouffee',
price: 13.99,
foodImages: require('../images/crawfishEto.jpeg'),
checked: false,
},
],
id: 1,
},
];
Replace FlatList to SectionList and modify RestaurantCard is item of list in renderItem.
const renderRestaurentInfo = ({ section }) => (
// render restaurantName, images and businessAddress
)
const renderFoodItem = ({ item }) => (
// render meal, foodImages, price
)
<SectionList
sections={restaurantData}
renderSectionHeader={renderRestaurentInfo}
keyExtractor={(_, index) => index + ''}
renderItem={renderFoodItem}
/>

Related

React native single selectable components

I am trying to achieve a simple single selectable item, as shown in the image below.
Right now, I have created an array of my data items and using .map to render the components because there are only 3-4 items max, now I want to select only a single item and change the color on the basis, and if I select any other item, it should unselect all the other items but not the current single selected item/component. I tried to do this but I am able to select all of them, obviously. Below is the code:
const items = [
{
id: 1,
iconName: 'male',
title: 'Male',
name: 'male',
},
{
id: 2,
iconName: 'female',
title: 'Female',
name: 'female',
},
{
id: 3,
iconName: 'transgender',
title: 'Others',
name: 'others',
},
];
const Component = ({dispatch, iconName, title, name}) => {
const [isSelected, setIsSelected] = useState(false);
return (
<TouchableOpacity
activeOpacity={0.6}
style={
isSelected
? [styles.selectable, {backgroundColor: 'green'}]
: [styles.selectable, {backgroundColor: COLORS.PINK}]
}
onPress={() => {
setIsSelected(!isSelected);
}}>
<View style={styles.row}>
<Ionicon name={iconName} size={36} color="#fff" />
<Text>{title}</Text>
</View>
</TouchableOpacity>
);
};
const Gender = () => {
return (
<View>
<View>
<Text>Your Gender</Text>
<View>
{items.map(item => (
<Component
key={item.id}
title={item.title}
iconName={item.iconName}
/>
))}
</View>
</View>
</View>
);
};
All though I could solve this by using separate states for each button, so whenever one is selected/pressed, the other states should become false. But then I would have to render individual component without using the .map method which I find inefficient. Can someone provide any solution based on my current approach to this problem?
Thank you!
Consider moving isSelected to the parent component, and instead of storing a booolean, store the selected item id. Pass the itemId, selectedId, setSelectedId (as a callback) to the child components and change the style check to:
style={
itemId === selectedId
? [styles.selectable, {backgroundColor: 'green'}]
: [styles.selectable, {backgroundColor: COLORS.PINK}]
}
onPress={() => {
setSelectedId(itemId);
}}>
Now you can get rid of keeping track whether the item is selected in the component, and only worry about it in the context of the parent (as it should be).
const Gender = () => {
const [selectedId, setSelectedId] = useState(false);
return (
<View>
<View>
<Text>Your Gender</Text>
<View>
{items.map(item => (
<Component
key={item.id}
itemId={item.id}
selectedId={selectedId}
setSelectedId={setSelectedId}
title={item.title}
iconName={item.iconName}
/>
))}
</View>
</View>
</View>
);
};

pass data between screens with getParamas

I'm rendering a few items in my map in ContactListand upon clicking on the thumbnail, I want to navigate to a new screen UserDetailsScreen such that the data about the clicked item is also passed along.
Previously I was using modals, but now I trying to switch to react-navigation.
ContactList.tsx:
export const ContactList: React.FunctionComponent<UserProps> = ({
data,
onDeleteContact,
}) => {
const [isUserVisible, setIsUserVisible] = useState(false);
//const [visibleUser, setVisibleUser] = useState<any>();
const navigation = useNavigation();
return (
<View style={styles.users}>
{data.users.nodes[0].userRelations.map(
(item: { relatedUser: RelatedUser; id: number }) => {
const numberOfFriends = item.relatedUser.userRelations.length;
const numberPlate = 'WHV AB 123';
return (
<View style={styles.item} key={item.id}>
{/* <TouchableOpacity onPress={() => setIsUserVisible(true)}> */}
<TouchableOpacity
onPress={() =>
navigation.navigate('UserDetailsScreen', {
firstName: item.relatedUser.firstName,
rating: item.relatedUser.rating,
numberOfFriends: numberOfFriends,
onDeleteContact: onDeleteContact,
isUserVisible: isUserVisible,
setIsUserVisible: setIsUserVisible,
numberPlate: numberPlate,
navigation: navigation,
})
}>
<Thumbnail
}}></Thumbnail>
</TouchableOpacity>
<View style={styles.nameNumber}>
<Text style={styles.userName}>{userName}</Text>
</View>
{/* <UserDetails
firstName={item.relatedUser.firstName}
rating={item.relatedUser.rating}
numberOfFriends={numberOfFriends}
onDeleteContact={onDeleteContact}
isUserVisible={isUserVisible}
setIsUserVisible={setIsUserVisible}
numberPlate={numberPlate}>
</UserDetails> */}
</View>
);
},
)}
</View>
);
};
UserDetailsScreen:
export const UserDetailsScreen: React.FunctionComponent<UserProps> = ({
firstName,
rating,
numberOfFriends,
numberPlate,
onDeleteContact,
navigation,
// isUserVisible,
// setIsUserVisible,
}) => {
//const navigation = useNavigation();
const fName = navigation.getParam('firstName')
return (
// <Modal visible={isUserVisible}>
<View style={styles.container}>
<View>
<TouchableOpacity
style={styles.cross}
//onPress={() => setIsUserVisible(false)}>
onPress={() => navigation.navigate('Whitelist')}>
<Thumbnail></Thumbnail>
</TouchableOpacity>
</View>
<View style={styles.searchLocationContainer}>
<UserInfoContainer
firstName={firstName}
rating={rating}
numberPlate={numberPlate}
numberOfFriends={numberOfFriends}></UserInfoContainer>
</View>
</View>
// </Modal>
);
};
Similarly, when I click on the thumbnail on this screen, I want to go back to my previous page where I can click on another object.
However, I keep getting an error that navigation.getParam is undefined. How can I fix this?
Hi you will get the data sent in the navigation params in
props.route.params

Struggling with controlling state in modal react-native - data not refreshing

I'm building a basic nutrition app that shows user's info regarding items they searched.
However, when my user selects an item from the flat list, I have a modal component that pops up and shows more info regarding the item. However, when I press back on my modal and select a new item on the FlatList, the data is remaining the same from the first item I pressed :(
export default class Tracker extends React.Component {
static navigationOptions = {
title: "Tracker",
};
//storing results from the api into this local state
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: null,
show: false,
};
}
`
fetchData = (item) => {
console.log(item);
fetch(
`https://api.edamam.com/api/food-database/parser?
ingr=${item}&app_id=${APP_ID}&app_key=${APP_KEY}`
)
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
// console.log(responseJson.hints[1].food.nutrients);
this.setState({
// passing in all the hints info into itemArray, which contains all the info regarding
the items
itemArray: responseJson.hints,
});
})
.catch((error) => {
console.log(error);
});
// dimisses keyboard if they press the button on the screen
Keyboard.dismiss();
};
<Button
title="Search"
onPress={() => this.fetchData(this.state.item)}
/>``
<View style={styles.ViewFilterContainer}>
<TouchableOpacity style={styles.ViewFilterContainer}>
<View style={styles.filterButtonView}>
<Text style={styles.filterText}> Filter </Text>
</View>
</TouchableOpacity>
</View>
<View style={styles.paddingForResultsContainer}>
<FlatList
style={styles.resultsBackground}
data={this.state.itemArray}
renderItem={({ item, index }) => (
<TouchableOpacity
onPress={() => this.setState({
show: true
})} //() => navigate("foodInfo")
>
<View style={styles.resultsContainer}>
<View style={styles.textView}>
<Text style={styles.resultsText}>
{item.food.label}
{item.food.brand}
</Text>
</View>
<View style={styles.nutritionResultsText}>
<Text style={styles.resultsTextSubInfo}>
F: {Math.round(item.food.nutrients.FAT)}
</Text>
<Text style={styles.resultsTextSubInfo}>
C: {Math.round(item.food.nutrients.CHOCDF)}
</Text>
<Text style={styles.resultsTextSubInfo}>
P: {Math.round(item.food.nutrients.PROCNT)}
</Text>
<Text style={styles.resultsTextSubInfo}>
K/Cal: {Math.round(item.food.nutrients.ENERC_KCAL)}
</Text>
</View>
</View>
<Modal transparent={true} visible={this.state.show}>
<View style={styles.modalView}>
<View>
<Text>{item.food.brand}</Text>
</View>
<Button title="Back" onPress={() => this.setState({show:false})}/>
</View>
</Modal>
</TouchableOpacity>
)}
/>
</View>

React Native navigation get param as an array and map the array

I need to change data between 2 different screens using react-navigation. The default screen shows the notes and adding the note is done in a separate screen using text input. Adding screen needs to give an array to default screen.
I managed to give some parameter but adding screen should pass an array.
Adding note class. Text input makes a new addition to notes state.
class AddScreen extends React.Component {
constructor(props) {
super(props);
}
state = {
textInputComponent: '',
notes: [
{
text: "Note 1"
}
]
}
return(
<View>
<TextInput style = {styles.inputField} placeholder="Write the note here"
onChangeText={(textInputComponent) => this.setState({textInputComponent})}
value = {this.state.textInputComponent} />
<TouchableOpacity style = {styles.customBtn2}
onPress={() => {this.addToList(this.state.textInputComponent)}}>
<Text style = {styles.customBtnText}>Add note </Text>
</TouchableOpacity>
<TouchableOpacity style = {styles.customBtn3}
onPress={() => navigate('Notes', {note: this.state.notes})}>
<Text style = {styles.customBtnText2}>Add notes </Text>
</TouchableOpacity>
</View>
);
The default screen where notes should be showing
class NoteList extends React.Component {
constructor(props) {
super(props)
this.state = {
notes: []
}
}
makeNoteList(note){
this.state.notes.push(note);
console.log(this.state.notes);
return (
<View>
{this.state.notes.map(note => {console.log(note.text)})}
</View>
)
}
render() {
const {navigate} = this.props.navigation;
const {navigation} = this.props;
const note = navigation.getParam('note', 'No notes');
return(
<ScrollView style = {styles.background}>
<View style={{flex: 1,
flexDirection: 'column',
alignItems: 'center'}}>
{this.makeNoteList(note)}
</View>
<View style={{flex: 1,
flexDirection: 'row',
justifyContent: 'flex-end'}} >
<TouchableOpacity style = {styles.customBtn}
onPress={() => navigate('AddNotes', {name: 'AddNotes'})}>
<Text style = {styles.customBtnText}>List of notes </Text>
</TouchableOpacity>
</View>
</ScrollView>
)
}
}
I can't seem to map the parameters that AddScreen class gives. Default screen should map this given array to show notes. I'm aware that these code snippets are missing some curly brackets and maybe brackets to shorten these snippets so here you can see the whole code: https://snack.expo.io/rk4opjbqE

React Native - Calling Flatlist item outside the Flatlist itself

I'm trying to have a Send Order Button below my FLATLIST where this Send Order Button will send the states to the other screen/view together with the item in my Flatlist.
"But the problem is I don't know how to call the Flatlist's item outside the Flatlist itself."
Here's my code
export default class Settlement extends Component {
constructor(props){
super(props)
this.state = {
......
}
fetchData = async () => {
....
this.setState({ data: json })
}
render() {
return (
<View>
<Text>Table No: { this.state.tbl }</Text>
<Text>{ this.state.DineIn }{ this.state.TakeOut }</Text>
<FlatList
data = {this.state.data}
renderItem = {({ item }) =>
<View>
<View>
<Text>{ item.item.order_id }</Text>
<Text>Name: { item.menu_name }</Text> //Flatlist_Items
<Text>Price: ₱{ item.menu_price }</Text>
<Text>Discount: { item.order_discount }</Text>
<Text>Amount: ₱{ item.order_amount }</Text>
.....
<Text>{ item.spcl_req }</Text>
<Text>{ item.order_quantity }</Text>
.....
</View>
</View>
}/>
</View>
<Text>Number of Orders: { this.state.numOrder }</Text>
<Text>Total Amount: ₱{ this.state.TotalSum }</Text>
<TouchableOpacity
onPress = { () => this.props.navigation.navigate('FinalSettlement', {
orderID : item.order_id,
Name : item.menu_name,
Price : item.menu_price, //Trying to pass
Ord_QTY : item.order_quantity, // the items to other view
spcl_req : item.spcl_req,
Discount : item.order_discount,
Amount : item.order_amount,
tbl : this.state.tbl,
pax : this.state.pax,
DineIn : this.state.DineIn,
TakeOut : this.state.TakeOut,
numOrder : this.state.numOrder,
TotalSum : this.state.TotalSum,
userName : this.state.userName
})}>
<Text>SEND ORDER</Text>
</TouchableOpacity>
</View>
</View>
)
}
}
Please look at the RN doc, list item should be separate component as mentioned in doc
_renderItem = ({item}) => (
<MyListItem
id={item.id}
onPressItem={this._onPressItem}
selected={!!this.state.selected.get(item.id)}
title={item.title}
/>
);
render() {
return (
<FlatList
data={this.props.data}
extraData={this.state}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
);
}
And instead of passing a lot of param in navigation function you can use global statement, for example Redux.
In your case you can get params in your screen 'FinalSettlement' by getParam function from ReactNavigation

Categories

Resources