how to set modal visible according to id? - reactjs

I have a map() data each of which has touchableopacity that should set the state of modal visible to true, but am unable to do it.
Could anyone let me know a way to set the state according to id?
ParentComponent:
export default function ExpandedVaccineComponent(props) {
const {navigation} = props;
const [note, setNote] = useState({});
const [addNoteText, setAddNoteText] = useState('');
const [modalVisible, setModalVisible] = useState(false);
const [selectedItem,setSelectedItem]=useState(null);
const [demo, setDemo] = useState(false);
let id = props.id;
return (
<View
style={{
backgroundColor: '#fff',
elevation: 3,
margin: '2%',
borderRadius: 5,
}}>
<View style={{marginTop: '2%'}}>
{props.item.vaccine_list.map((i) => {
return (
<View style={{flex: 1}}>
<View style={{flexDirection: 'row'}}>
<TouchableOpacity onPress={() => setModalVisible(true)}>
<Icon name="calendar" size={15} />
</TouchableOpacity>
<View>
<Modal
animationType="fade"
transparent={true}
visible={modalVisible}>
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
borderRadius: 20,
}}>
<CalenderModal
item={props.item}
modalVisible={modalVisible}
item={i}
id={id}
/>
<View
style={{
flexDirection: 'row',
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
width: '72%',
}}>
<TouchableOpacity
style={{
flex: 1,
borderTopColor: '#C0C0C0',
borderTopWidth: 1,
borderRightColor: '#C0C0C0',
borderRightWidth: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 10,
}}
onPress={() => setModalVisible(!modalVisible)}>
<Text
style={{
fontFamily: 'Roboto',
fontSize: 17,
fontWeight: 'bold',
color: '#2D7AFA',
}}>
Cancel
</Text>
</TouchableOpacity>
<TouchableOpacity
style={{
flex: 1,
borderTopColor: '#C0C0C0',
borderTopWidth: 1,
borderLeftColor: '#C0C0C0',
borderLeftWidth: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 10,
}}>
<Text
style={{
fontFamily: 'Roboto',
fontSize: 17,
fontWeight: 'bold',
color: '#2D7AFA',
}}>
Save
</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
</View>
<View style={{marginLeft: '10%', width: '30%'}}>
<View>
<View style={{flexDirection: 'row'}}>
<Text
style={{
fontFamily: 'Roboto',
fontSize: 14,
color: '#000',
}}>
{i.name}
</Text>
<Text
style={{
fontFamily: 'Roboto',
color: '#000',
opacity: 0.5,
}}>
(Dose{i.dose}/3)
</Text>
</View>
<Text
numberOfLines={2}
ellipsizeMode="tail"
style={{
fontFamily: 'Roboto',
fontSize: 14,
lineHeight: 14,
color: '#000',
opacity: 0.5,
}}>
{i.protects_against}
</Text>
</View>
</View>
<View style={{marginLeft: '30%'}}>
<Text>{i.child_vacc_status.text}</Text>
<Text>16 Aug,2020</Text>
</View>
</View>
</View>
);
})}
</View>
</View>
);
}
CalenderModal component:
import React, {useState} from 'react';
import {View, Text, StyleSheet, TouchableOpacity, Image} from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
import DatePicker from 'react-native-datepicker';
import axios from 'axios';
import {ROOT} from '../../../ApiUrl';
export default function CalenderModal(props) {
const [date, setDate] = useState('2016-05-15');
const [vaccinatedOn, setVaccinatedOn] = useState(null);
const [reminder, setReminder] = useState(1);
const [toggle, setToggle] = useState(null);
const [modalVisible, setModalVisible] = useState(null);
const reminderSet = () => {
setReminder(6);
setToggle(6);
};
const vaccinationSet = () => {
setVaccinatedOn(1);
setToggle(1);
};
const data = props.item;
const id = props.id;
const SaveData = () => {
let url = `some url`;
axios({
method: 'POST',
url: url,
data: {
child_id: id,
event_date: date,
status: toggle,
vaccine_id: data.id,
},
headers: {
'content-type': 'application/json',
},
})
.then(function (res) {
console.log('RES', res);
if (res.data.code == 200) {
navigation.navigate('consultationHome', {res: res});
}
})
.catch(function (err) {
console.log('ERR', err);
});
};
return (
<View>
<View style={styles.modalView}>
<Text>{props.item.name}</Text>
<View
style={{
flexDirection: 'row',
justifyContent: 'space-between',
marginTop: '5%',
}}>
{reminder == 6 && toggle == 6 ? (
<TouchableOpacity style={{flexDirection: 'row'}}>
<Icon
name="circle"
size={17}
color="pink"
style={{marginTop: '2%', paddingRight: '1%'}}
/>
<Text
style={{
fontFamily: 'Roboto',
fontSize: 17,
color: '#000',
}}>
Set reminder
</Text>
</TouchableOpacity>
) : (
<TouchableOpacity
style={{flexDirection: 'row'}}
onPress={() => reminderSet()}>
<Icon
name="circle-o"
size={17}
color="#000"
style={{marginTop: '2%', paddingRight: '1%'}}
/>
<Text
style={{
fontFamily: 'Roboto',
fontSize: 17,
color: '#000',
}}>
Set reminder
</Text>
</TouchableOpacity>
)}
{vaccinatedOn == 1 && toggle == 1 ? (
<TouchableOpacity style={{flexDirection: 'row'}}>
<Icon
name="circle"
size={17}
color="pink"
style={{marginTop: '2%', paddingRight: '1%'}}
/>
<Text
style={{
fontFamily: 'Roboto',
fontSize: 17,
color: '#000',
}}>
Vaccinated on
</Text>
</TouchableOpacity>
) : (
<TouchableOpacity
style={{flexDirection: 'row'}}
onPress={() => vaccinationSet()}>
<Icon
name="circle-o"
size={17}
color="#000"
style={{marginTop: '2%', paddingRight: '1%'}}
/>
<Text
style={{
fontFamily: 'Roboto',
fontSize: 17,
color: '#000',
}}>
Vaccinated on
</Text>
</TouchableOpacity>
)}
</View>
<DatePicker
style={{width: 200, marginTop: '5%'}}
date={date}
mode="date"
placeholder="select date"
format="YYYY-MM-DD"
minDate="2016-05-01"
maxDate="2016-06-01"
confirmBtnText="Confirm"
cancelBtnText="Cancel"
customStyles={{
dateIcon: {
position: 'absolute',
left: 0,
top: 4,
marginLeft: 0,
},
dateInput: {
marginLeft: 36,
},
// ... You can check the source to find the other keys.
}}
onDateChange={(date) => {
setDate(date);
}}
/>
{console.log('DATE', date)}
</View>
</View>
);
}
const styles = StyleSheet.create({
modalView: {
backgroundColor: 'white',
padding: 20,
},
});
Any suggestion would be great, please let me know if anything else is required for better understanding.

you might want to try setting up your onPress similar to the RN documentation for TouchableOpacity (https://reactnative.dev/docs/touchableopacity.html you also may want to consider switching to Pressable for future proofing purposes). You can do
const onPress = () => setModalVisible(true);
<TouchableOpacity onPress={onPress}>

Related

React Native Custom Drawer Navigation (navigation.closeDrawer())

I'm new to react native i've been trying to create a custom drawer. Inside i wanted to add a button that would close the drawer however i've been getting the error TypeError: undefined is not an object (evaluating 'navigation.closeDrawer') this is my code:
import React, { useState } from "react";
import {DrawerActions} from '#react-navigation/native';
import Logo from '../assets/images/Logo.png'
import {
View,
Text,
Image,
TouchableOpacity,
ScrollView
} from 'react-native';
import {
createDrawerNavigator,
DrawerContentScrollView,
} from '#react-navigation/drawer';
import { MainLayout } from "../screens";
import {
COLORS,
FONTS,
SIZES,
constants,
icons,
dummyData,
} from '../constants';
import Animated from "react-native-reanimated";
const Drawer = createDrawerNavigator()
const CustomDrawerItem = ({label, icon}) => {
return (
<TouchableOpacity
style={{
flexDirection: 'row',
height: 40,
marginBottom: SIZES.base,
alignItems: 'center',
paddingLeft: SIZES.radius,
borderRadius: SIZES.base
}}
>
<Image
source={icon}
style={{
width: 20,
height: 20,
tintColor: COLORS.primary
}}/>
<Text
style={{
marginLeft: 15,
color: COLORS.primary,
...FONTS.h3
}}
>
{label}
</Text>
</TouchableOpacity>
)
}
const CustomDrawerContent = ({ navigation }) => {
return (
<DrawerContentScrollView
scrollEnbled={true}
contentContainerStyle={{ flex: 1}}
>
<ScrollView
style={{
flex: 1,
paddingHorizontal: SIZES.radius
}}
>
{/* Logo*/}
<TouchableOpacity
style = {{
flexDirection: 'row',
marginTop: SIZES.radius,
alignItems: 'center',
marginBottom: 20,
}}>
<Image style={{
flex: 1,
width: 80,
height: 80,
resizeMode: 'contain',
marginLeft: -60,
}}
source={Logo}/>
<View
style={{
marginLeft: SIZES.radius
}}
>
<View
style={{
alignItems: 'flex-end',
justifyContent: 'center'
}}
>
<TouchableOpacity
style={{
alignItems: 'center',
justifyContent: 'center',
tintColor: COLORS.black
}}
onPress={() => navigation.closeDrawer()}
>
<Image
source={icons.cross}
style={{
height: 25,
width: 25,
}}
/>
</TouchableOpacity>
</View>
</View>
</TouchableOpacity>
{/* Profile */}
<TouchableOpacity
style = {{
flexDirection: 'row',
marginTop: SIZES.radius,
alignItems: 'center'
}}>
<View
style={{
marginLeft: SIZES.radius
}}
>
<CustomDrawerItem
icon={icons.profile}
label={constants.sideBarElements.guest}
/>
<View>
{dummyData.GuestData.map(
(item, index) => (
<Text
style={{
paddingLeft: 20,
paddingTop: 10,
color: COLORS.gray
}}>
{item.name}
</Text>
)
)}
</View>
</View>
</TouchableOpacity>
{/* Line devider
<View
style={{
height: 1,
marginVertical: SIZES.radius,
//marginLeft: SIZES.radius,
backgroundColor: COLORS.lightGray1
}}
/>*/}
{/* Drawer Items */}
<View
style={{
flex: 1,
marginTop: SIZES.padding,
marginLeft: SIZES.radius
}}
>
<CustomDrawerItem
icon={icons.cart}
label={constants.sideBarElements.store}
/>
<View>
{dummyData.storesData.map(
(item, index) => (
<Text
style={{
paddingLeft: 20,
paddingTop: 10,
color: COLORS.gray
}}>
{item.title}
</Text>
)
)}
</View>
</View>
<View
style={{
flex: 1,
marginTop: SIZES.padding,
marginLeft: SIZES.radius,
marginBottom: 50
}}
>
<CustomDrawerItem
icon={icons.cart}
label={constants.sideBarElements.categories}
/>
<View>
{dummyData.carouselData.map(
(item, index) => (
<Text
style={{
paddingLeft: 20,
paddingTop: 10,
color: COLORS.gray
}}>
{item.category}
</Text>
)
)}
</View>
</View>
</ScrollView>
</DrawerContentScrollView>
)
}
const CustomDrawer = () => {
const [progress, setProgress] = useState(new Animated.Value(0))
const scale = Animated.interpolateNode(progress, {
inputRange: [0, 1],
outputRange: [1, 0.8]
})
const borderRadius = Animated.interpolateNode(progress, {
inputRange: [0, 1],
outputRange: [0, 26]
})
const animatedStyles = {borderRadius, transform: [{scale}]}
return (
<View
style={{
flex: 1,
backgroundColor: COLORS.primary
}}
>
<Drawer.Navigator
drawerType='slide'
overlayColor='transparent'
drawerStyle={{
flex: 1,
width: '65%',
paddingRight: 20,
backgroundColor: 'transparent'
}}
sceneContainerStyle={{
backgroundColor: 'transparent'
}}
initialRouteName='MainLayout'
drawerContent={props => {
setTimeout(()=> {
setProgress(props.progress)
}, 0)
return (
<CustomDrawerContent
navigration ={props.navigation}
/>
)
}}
>
<Drawer.Screen name='MainLayout'>
{props => <MainLayout {...props} drawerAnimationStyle={animatedStyles}/>}
</Drawer.Screen>
</Drawer.Navigator>
</View>
)
}
export default CustomDrawer;
i even tried it with dispatch but i get the same error i'm not sure what i'm doing wrong.
Try this
import {useNavigation} from '#react-navigation/native';
const navigation = useNavigation();
navigation.openDrawer();
navigation.closeDrawer();

Remove data/memory for particular week Day of array if data(Time) is empty for that index from double map

I am making Learn Reminder App for that I am mapping a static array of weeks and inside that map i am mapping another array(reminderData) which i am fetching it from database to compare both array and if the weekDay matches with the Parent Map that is weeks array than I am just showing the time. But due to my turnary Operator condition my data is mixed with Set Time and data present in database. How do I optimize my code to show time for that particular week if present in db or show set time instead.
const weeks = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"];
<View style={{ flex: 1, borderWidth: 1, borderRadius: 10, marginTop: "3%", height: "50%" }}>
{
weeks.length > 0 &&
weeks.map((week, index) => (
<View key={index}>
<View style={{ flexDirection: 'row', marginHorizontal: "5%", flexDirection: 'row' }}>
<Text style={{ flex: 1, paddingTop: 13, color: 'black', fontSize: 18, fontWeight: 'bold' }} >{week}</Text>
{
reminderData.map((data, index2) => (
<React.Fragment key={index2}>
{
(data.WeekDay == week) ?
<Pressable onPress={() => updateTimePicker(data, index)}>
<Text onPress={() => updateTimePicker(data, index)} key={index} style={{ justifyContent: 'flex-end', paddingTop: 13, color: 'black', fontSize: 18, }}>{moment(data.Time).format('hh:mm A')}</Text>
</Pressable> :
<Pressable onPress={() => openTimePicker(week, index)}>
<Text onPress={() => openTimePicker(week, index)} key={index} style={{ justifyContent: 'flex-end', paddingTop: 13, color: 'black', fontSize: 18, }}>Set Time</Text>
</Pressable>
}
</React.Fragment>
))
}
</View>
<View
style={{
marginTop: 8,
borderBottomColor: 'black',
borderBottomWidth: StyleSheet.hairlineWidth,
}}
/>
</View>
))
}
</View>
try this:
import React,{useState,useEffect} from 'react';
import moment from 'moment'
import { Text, View, StyleSheet,FlatList,Pressable,Image } from 'react-native';
const WeekComponent = ({ week,reminderData }) => {
const [data,setData]=useState({})
useEffect(()=>{
const foundData=reminderData.find(data=>data.weekDay ===week) //you can use filter if you want to return all data that have weekDay===week
setData(foundData)
},[reminderData,week])
return (
<View>
<View style={{ flexDirection: 'row', marginHorizontal: "5%", flexDirection: 'row' }}>
<Text style={{ flex: 1, paddingTop: 13, color: 'black',
fontSize: 18, fontWeight: 'bold' }} >
{week}
</Text>
{data ? (
<Pressable onPress={() => updateTimePicker(data)}>
<Text onPress={() => updateTimePicker(data)}
style={{ justifyContent: 'flex-end', paddingTop: 13,
color: 'black', fontSize: 18, }}>
{moment(data?.Time).format('hh:mm A')}
</Text>
</Pressable>
):(
<Pressable onPress={() => openTimePicker(week)}>
<Text onPress={() => openTimePicker(week)}
style={{ justifyContent: 'flex-end', paddingTop: 13,
color: 'black', fontSize: 18, }}>
Set Time
</Text>
</Pressable>
)}
</View>
</View>
);
};
export default function App() {
const weeks = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday","Sunday"];
const reminderData=[{weekDay:'',time:''},{weekDay:'',time:''},{weekDay:'',time:''}]
return (
<View >
<FlatList
data={weeks}
renderItem={({ item }) => <WeekComponent week={item} reminderData={reminderData}/> }
// keyExtractor={(item) => item.id}
/>
</View>
);
}

how to update state according to id?

I have a touchableopacity on each card where I want to setstate of expand to true, but I want to do it according to the id, so that state of only one changes, any idea how to do it using map()?
My code:
import React, {useState, useEffect} from 'react';
import {
SafeAreaView,
Text,
Image,
ScrollView,
TouchableOpacity,
View,
} from 'react-native';
import axios from 'axios';
import {ROOT} from '../../../../ApiUrl';
import Icon from 'react-native-vector-icons/FontAwesome';
export default function VaccinationListScreen(props) {
const [expand, setExpand] = useState(false);
const [data, setData] = useState('');
let id = props.route.params.id;
const getData = () => {
let url = `some url`;
console.log('bbb');
axios
.get(url)
.then(function (res) {
console.log(res.data.content);
setData(res.data.content);
})
.catch(function (err) {
console.log(err);
});
};
useEffect(() => {
getData();
}, []);
return (
<SafeAreaView>
<ScrollView>
<TouchableOpacity style={{padding: 10}} onPress={()=>setExpand(true)}>
{data != undefined &&
data != null &&
data.map((item) => {
return (
<View
style={{
padding: 10,
backgroundColor: '#fff',
elevation: 3,
margin: '2%',
borderRadius: 5,
}}
key={item.id}>
<View style={{alignItems: 'flex-end'}}>
<Text style={{color: 'grey', fontSize: 12}}>
{item.display_date}
</Text>
</View>
<View style={{flexDirection: 'row'}}>
<View>
<Image
source={require('../../assets/atbirth.jpg')}
style={{height: 40, width: 50}}
resizeMode="contain"
/>
</View>
<View style={{flex: 1}}>
<View style={{flexDirection: 'row', flex: 1}}>
<Text
key={item.id}
style={{
fontFamily: 'Roboto',
fontSize: 18,
fontWeight: 'bold',
}}>
{item.name}
</Text>
</View>
<View style={{flexDirection: 'row', width: '30%'}}>
{item.vaccine_list.map((i) => {
return (
<View style={{flexDirection: 'row'}}>
<Text
numberOfLines={1}
ellipsizeMode="tail"
style={{fontFamily: 'Roboto', fontSize: 15}}>
{i.name},
</Text>
</View>
);
})}
</View>
</View>
</View>
<View style={{alignItems: 'flex-end', marginTop: '1%'}}>
<View style={{flexDirection: 'row'}}>
<Text
style={{
color: 'red',
fontSize: 14,
fontWeight: 'bold',
}}>
{item.child_vacc_status.text}
</Text>
<Icon
name="chevron-up"
color="red"
size={12}
style={{marginTop: '1%', marginLeft: '1%'}}
/>
</View>
</View>
</View>
);
})}
</TouchableOpacity>
</ScrollView>
</SafeAreaView>
);
}
Any suggestions would be great, do let mw know if anything else is required for better understanding
As i review your code <TouchableOpacity> wraps all of your cards at once not on each card set. If you implement your code that way if it's not impossible it will be difficult for you to reference each cards id and set the state of expand to true according to cards id.
My suggestion is to include <TouchableOpacity> to map() function nest so that it will be easy to reference each cards function.
I reproduce this specific problem and implement a solution in which I was able to set the state of expand to true according to each cards id.
You may click the sandbox link to see a demonstration.
https://codesandbox.io/s/accordingtoid-4px1w
Code in Sandbox:
import React, { useState, useEffect } from "react";
import {
SafeAreaView,
Text,
Image,
TouchableOpacity,
View
} from "react-native";
// import axios from 'axios';
// import {ROOT} from '../../../../ApiUrl';
// import Icon from "react-native-vector-icons/FontAwesome";
export default function VaccinationListScreen(props) {
const [expand, setExpand] = useState({});
const [data, setData] = useState([]);
// let id = props.route.params.id;
// const getData = () => {
// let url = `some url`;
// console.log('bbb');
// axios
// .get(url)
// .then(function (res) {
// console.log(res.data.content);
// setData(res.data.content);
// })
// .catch(function (err) {
// console.log(err);
// });
// };
// useEffect(() => {
// getData();
// }, []);
useEffect(() => {
// In order to simulate and reproduce the problem
// Assume that these are the data that you fetch from an API
const dataContent = [
{
id: 1,
name: "At Birth",
display_date: "02 May - 08 May 16",
vaccine_list: [
{ name: "BCG" },
{ name: "Hepatitis B" },
{ name: "OPV 0" }
],
child_vacc_status: { text: "Missed" }
},
{
id: 2,
name: "At 6 Weeks",
display_date: "02 May - 08 May 16",
vaccine_list: [
{ name: "IPV" },
{ name: "PCV" },
{ name: "Hepatitis b" },
{ name: "DTP" },
{ name: "HiB" },
{ name: "Rotavirus" }
],
child_vacc_status: { text: "Missed" }
}
];
setData(dataContent);
}, []);
function handleOnPress(id) {
setExpand((prev) => {
let toggleId;
if (prev[id]) {
toggleId = { [id]: false };
} else {
toggleId = { [id]: true };
}
return { ...toggleId };
});
}
useEffect(() => {
console.log(expand); // check console to see the value
}, [expand]);
return (
<SafeAreaView>
{data !== undefined &&
data !== null &&
data.map((item) => {
return (
<TouchableOpacity
key={item.id}
style={{
padding: 10
}}
onPress={() => handleOnPress(item.id)}
>
<View
style={{
padding: 10,
backgroundColor: expand[item.id] ? "lightgrey" : "#fff",
elevation: 3,
margin: "2%",
borderRadius: 5
}}
>
<View style={{ alignItems: "flex-end" }}>
<Text style={{ color: "grey", fontSize: 12 }}>
{item.display_date}
</Text>
</View>
<View style={{ flexDirection: "row" }}>
<View>
<Image
// source={require('../../assets/atbirth.jpg')}
style={{ height: 40, width: 50 }}
resizeMode="contain"
/>
</View>
<View style={{ flex: 1 }}>
<View style={{ flexDirection: "row", flex: 1 }}>
<Text
key={item.id}
style={{
fontFamily: "Roboto",
fontSize: 18,
fontWeight: "bold"
}}
>
{item.name}
</Text>
</View>
<View style={{ flexDirection: "row", width: "30%" }}>
{item.vaccine_list.map((item, i) => {
return (
<View key={i} style={{ flexDirection: "row" }}>
<Text
numberOfLines={1}
ellipsizeMode="tail"
style={{ fontFamily: "Roboto", fontSize: 15 }}
>
{item.name},
</Text>
</View>
);
})}
</View>
</View>
</View>
<View style={{ alignItems: "flex-end", marginTop: "1%" }}>
<View style={{ flexDirection: "row" }}>
<Text
style={{
color: "red",
fontSize: 14,
fontWeight: "bold"
}}
>
{item.child_vacc_status.text}
</Text>
</View>
</View>
</View>
</TouchableOpacity>
);
})}
</SafeAreaView>
);
}
I haven't tested the code to work correctly, but you could try something similar. You could create a separate component for the items and set a status for each of them.
export default function VaccinationListScreen(props) {
const [expand, setExpand] = useState(false);
const [data, setData] = useState("");
const VaccinationListItem = (item) => {
const [expand, setExpand] = useState(false);
return (
<TouchableOpacity style={{ padding: 10 }} onPress={() => setExpand(true)}>
<View
style={{
padding: 10,
backgroundColor: "#fff",
elevation: 3,
margin: "2%",
borderRadius: 5,
}}
key={item.id}
>
<View style={{ alignItems: "flex-end" }}>
<Text style={{ color: "grey", fontSize: 12 }}>
{item.display_date}
</Text>
</View>
<View style={{ flexDirection: "row" }}>
<View>
<Image
source={require("../../assets/atbirth.jpg")}
style={{ height: 40, width: 50 }}
resizeMode="contain"
/>
</View>
<View style={{ flex: 1 }}>
<View style={{ flexDirection: "row", flex: 1 }}>
<Text
key={item.id}
style={{
fontFamily: "Roboto",
fontSize: 18,
fontWeight: "bold",
}}
>
{item.name}
</Text>
</View>
<View style={{ flexDirection: "row", width: "30%" }}>
{item.vaccine_list.map((i) => {
return (
<View style={{ flexDirection: "row" }}>
<Text
numberOfLines={1}
ellipsizeMode="tail"
style={{ fontFamily: "Roboto", fontSize: 15 }}
>
{i.name},
</Text>
</View>
);
})}
</View>
</View>
</View>
<View style={{ alignItems: "flex-end", marginTop: "1%" }}>
<View style={{ flexDirection: "row" }}>
<Text
style={{
color: "red",
fontSize: 14,
fontWeight: "bold",
}}
>
{item.child_vacc_status.text}
</Text>
<Icon
name="chevron-up"
color="red"
size={12}
style={{ marginTop: "1%", marginLeft: "1%" }}
/>
</View>
</View>
</View>
</TouchableOpacity>
);
};
return (
<SafeAreaView>
<ScrollView>
{data != undefined &&
data != null &&
data.map((item) => {
VaccinationListItem(item);
})}
</ScrollView>
</SafeAreaView>
);
}
Generally if you want to toggle any single element you should store its id in expand (instead of a boolean), and simply check when rendering the array if any specific element's id matches, i.e. element.id === expand. When any new element is touched, pop its id in there, if the id is already there, set to null to collapse.
export default function VaccinationListScreen(props) {
const [expandId, setExpandId] = useState(null); // <-- stores null or id, initially null
...
// Create curried handler to set/toggle expand id
const expandHandler = (id) => () =>
setExpandId((oldId) => (oldId === id ? null : id));
return (
<SafeAreaView>
<ScrollView>
{data &&
data.map((item) => {
return (
<View
...
key={item.id}
>
<TouchableOpacity
style={{ padding: 10 }}
onPress={expandHandler(item.id)} // <-- attach callback and pass id
>
...
</TouchableOpacity>
{item.id === expandId && ( // <-- check if id match expand id
<ExpandComponent />
)}
</View>
);
})}
</ScrollView>
</SafeAreaView>
);
}

How to append new images to the array using react native image crop picker?

Hi, I am using react-native-image-crop-picker to overcome the above-shown module to select images from the gallery and display it in react native app, but I also want to click on add photo and again select images from the gallery and append them to the previous array of photos, am unable to figure that out.
This is the exact code that I have written to achieve the above-shown behavior, what should I change or add to perform the append feature?
import React, {useEffect, useState} from 'react';
import {
View,
Text,
StyleSheet,
ScrollView,
Image,
TouchableOpacity,
Dimensions,
FlatList,
} from 'react-native';
import Icon from 'react-native-vector-icons/FontAwesome';
import ImagePicker from 'react-native-image-crop-picker';
const deviceWidth = Dimensions.get('window').width;
const deviceHeight = Dimensions.get('window').height;
const App = () => {
const [photos, setAddPhotos] = useState(null);
const [photo, setAddphoto] = useState(null);
const handleChoosePhoto = () => {
ImagePicker.openPicker({
multiple: true,
waitAnimationEnd: false,
includeExif: true,
forceJpg: true,
})
.then((images) => {
setAddphoto(null);
setAddPhotos(
images.map((i) => {
console.log('recieved image', i);
return {
uri: i.path,
// width: i.width,
// height: i.height,
width: 185,
height: 128,
mime: i.mime,
};
}),
);
})
.catch((e) => alert(e));
};
const renderImage = (image) => {
return (
<Image
style={{
width: 185,
height: 128,
resizeMode: 'contain',
marginTop: 1,
}}
source={image}
/>
);
};
const renderAsset = (image) => {
return renderImage(image);
};
return (
<View style={{flex: 1}}>
{console.log('PHOTOS', photos)}
{photos === null ? (
<View style={{flex: 1}}>
<View style={Styles.headerWrapper}>
<View
style={{flexDirection: 'row', justifyContent: 'space-between'}}>
<Icon name="angle-left" size={30} />
<TouchableOpacity style={{right: '10%', top: '2%'}}>
<Text style={{fontSize: 15, fontWeight: 'bold'}}>
SAVE AND EXIT
</Text>
</TouchableOpacity>
</View>
</View>
<ScrollView>
<Text style={Styles.headerText}>Add photos to your listing</Text>
<Text style={Styles.subHeader}>
Photos help guests imagine staying in your place. You can start
with one and add more after you publish.
</Text>
<View style={Styles.container}>
<TouchableOpacity onPress={() => handleChoosePhoto()}>
<View
style={{
backgroundColor: '#20B2AA',
width: 150,
height: 40,
borderRadius: 5,
justifyContent: 'center',
}}>
<Text
style={{
color: '#fff',
textAlign: 'center',
fontSize: 15,
fontWeight: 'bold',
}}>
Add photos
</Text>
</View>
</TouchableOpacity>
</View>
</ScrollView>
<TouchableOpacity
style={{
alignSelf: 'flex-end',
right: '5%',
position: 'absolute',
bottom: 10,
}}>
<View
style={{
borderColor: '#20B2AA',
borderWidth: 1,
alignSelf: 'flex-end',
padding: 10,
}}>
<Text
style={{fontSize: 15, fontWeight: 'bold', color: '#20B2AA'}}>
Skip For Now
</Text>
</View>
</TouchableOpacity>
</View>
) : (
<>
<View style={{flex: 1}}>
<View style={Styles.headerWrapper}>
<View
style={{flexDirection: 'row', justifyContent: 'space-between'}}>
<Icon name="angle-left" size={30} />
<TouchableOpacity style={{right: '10%', top: '2%'}}>
<Text style={{fontSize: 15, fontWeight: 'bold'}}>
SAVE AND EXIT
</Text>
</TouchableOpacity>
</View>
</View>
<ScrollView>
<View style={{flex: 1, flexWrap: 'wrap', flexDirection: 'row'}}>
{photos
? photos.map((i) => (
<View
style={{
// width: 185, height: 128,
// width:'50%',
flexBasis: '33.33%',
}}
key={i.uri}>
{renderAsset(i)}
</View>
))
: null}
</View>
</ScrollView>
<TouchableOpacity
style={{
alignSelf: 'flex-end',
right: '5%',
position: 'absolute',
bottom: 10,
}}>
<View
style={{
backgroundColor: '#20B2AA',
alignSelf: 'flex-end',
padding: 10,
}}>
<Text style={{fontSize: 15, fontWeight: 'bold', color: '#FFF'}}>
NEXT
</Text>
</View>
</TouchableOpacity>
</View>
</>
)}
</View>
);
};
export default App;
const Styles = StyleSheet.create({
headerWrapper: {
width: deviceWidth,
paddingLeft: 24,
paddingTop: 10,
paddingBottom: 10,
},
headerText: {
fontWeight: 'bold',
fontSize: 28,
paddingLeft: 24,
},
container: {
padding: 24,
},
subHeader: {
paddingLeft: 24,
fontSize: 17,
paddingTop: 24,
paddingRight: 24,
},
});
Please let me know if anything else is required for better understanding, thank you.
You only need to take the prevState in setAddPhotos
setAddPhotos((lastPhotos) => {
const imagesMap = images.map((i) => {
return {
uri: i.path,
width: i.width,
height: i.height,
mime: i.mime,
};
});
return [...lastPhotos, ...imagesMap];
});

Display a Modal on a holding modal on react native

I was trying to display a modal on another model its visibility always set to true. I tried multiple ways but still I am unable to show that modal. I am printing here the code to make it a sense:
function Search({navigation}) {
const [number, setNumber] = useState('');
const [visible, setvisible] = useState(false);
return (
<View style={{flex: 1, backgroundColor: 'rgba(0,0,0,0.5)'}}>
<Modal
visible={true}
transparent={true}
style={{
elevation: 24,
borderRadius: 24,
marginTop: 60,
backgroundColor: '#fafcff',
}}>
<View
style={{
margin: 50,
padding: 10,
borderRadius: 35,
height: 40,
flexDirection: 'column',
justifyContent: 'center',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 12,
},
shadowOpacity: 0.58,
shadowRadius: 16.0,
elevation: 24,
}}>
<TextInput
label="Enter a phone number"
value={textInput}
keyboardType="numeric"
maxLength={12}
mode="outlined"
dense={true}
// ref={textInput.current.clear()}
theme={{
colors: {
primary: '#00aaff',
},
}}
onChangeText={(text) => {
setNumber(text);
}}
/>
<Button
mode="contained"
theme={{
colors: {
primary: '#006aff',
},
}}
style={{
margin: 70,
marginTop: 10,
padding: 2,
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 5,
},
shadowOpacity: 0.34,
shadowRadius: 6.27,
justifyContent: 'center',
elevation: 18,
borderRadius: 18,
textAlign: 'center',
}}
onPress={() => {
if (number === '') {
<Modal visible={!visible} transparent={true} animationType={'fade'}>
<View style={styles.mainOuterComponent}>
<View style={styles.mainContainer}>
{/* First ROw - Alert Icon and Title */}
<View style={styles.topPart}>
{true && (
<Image
source={require('../images/ic_notification.png')}
resizeMode={'contain'}
style={styles.alertIconStyle}
/>
)}
<Text style={styles.alertTitleTextStyle}>
{/* {`${alertTitleText}`} */} Here worked
</Text>
</View>
{/* Second Row - Alert Message Text */}
<View style={styles.middlePart}>
<Text style={styles.alertMessageTextStyle}>
{/* {`${alertMessageText}`} */} EMpty value
</Text>
</View>
{/* Third Row - Positive and Negative Button */}
<View style={styles.bottomPart}>
{true && (
<TouchableOpacity
// onPress={this.onPositiveButtonPress}
style={styles.alertMessageButtonStyle}>
<Text style={styles.alertMessageButtonTextStyle}>
{/* {positiveButtonText} */} Okay
</Text>
</TouchableOpacity>
)}
</View>
</View>
</View>
</Modal>
} else {
console.log('Do Nothing!');
}
}}>
<Text
style={{
fontSize: 25,
color: 'white',
textAlign: 'center',
}}>
{' '}
<MaterialCommunityIcons
name="database-search"
size={40}
color="yellow"
style={{
paddingRight: 5,
alignItems: 'center',
margin: 5,
}}></MaterialCommunityIcons>
Search
</Text>
</Button>
</Modal>
</View>
)
}
expected that when user clicks "search" while the input is empty, a modal showing empty value entered. I got nothing, no error. How can I solve this?
Make two components, then render it based on conditions inside one modal, I used this way for many components.
const ModalContent = ({toggleModal}) => {
const [defaultContent, setDefaultContent] = useState(true);
const conditionalRendering = ()=> {
if(defaultContent === true) return (
<View>
<TouchableOpacity onPress={()=> setDefaultContent(false)}>
<Text>First Content</Text>
</TouchableOpacity>
</View>
);
return (
<View>
<TouchableOpacity onPress={()=> setDefaultContent(true)}>
<Text>Second Content</Text>
</TouchableOpacity>
</View>
);
}
return (
<View style={{flex:1, justifyContent: 'center', alignItems: 'center'}}>
{conditionalRendering()}
</View>
);
}
<Modal
useNativeDriver
onBackButtonPress={toggleModal}
isVisible={modalVisible}
style={{ padding: 0, margin: 0, justifyContent: 'center' }}
>
<ModalContent toggleModal={toggleModal} />
</Modal>

Resources