Updating informations from modal-React native - javascript

On my react native app I display information that I fetched from the server this way:
So when I click update profil, I display a modal with text input on it in order to give the user the opportunity to change the information of his profile. The modal look like this:
Now I already created a Fetch Post function that, when I click on the button update it sends static information to the server and the modal closes. but the profile page doesn't refresh until I get out of it and come back.
My question is: whats the best way to get the values from the textinputs, send them through post. and refresh the screen after the modal closes?. Should i use formik?
Here is a look at my code:
export default function MprofilScreen({ navigation }) {
const [modalVisible, setModalVisible] = useState(false);
const [Data, setData] = useState([]);
useEffect(() => {
fetch('******')
.then((response) => response.json())
.then((res) => {
console.log("repooooonse")
console.log(res)
setData(res)
})
.done();
}, []);
return (
<View style={{ flex: 1, backgroundColor: 'white' }} >
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => {
Alert.alert("Modal has been closed.");
}}>
<View style={styles.modalView}>
<TouchableOpacity
style={{ ...styles.openButton, backgroundColor: "#2196F3" }}
onPress={() => {
setModalVisible(!modalVisible);
}}
>
<Text style={styles.textStyle}>close</Text>
</TouchableOpacity>
<ScrollView>
<Text style={styles.text}>Nom:</Text>
<TextInput style={styles.text_input} placeholder="nom" />
....
<Text style={styles.text}>Ville :</Text>
<TextInput style={styles.text_input} placeholder="Ville " />
<TouchableOpacity
style={{ ...styles.openButton, backgroundColor: "#2196F3" }}
onPress={() => {
setModalVisible(!modalVisible);
}}
>
<Text style={styles.textStyle}>Delete</Text>
</TouchableOpacity>
</ScrollView>
</View>
</Modal>
<TouchableOpacity style={styles.btn}
onPress={() => {
setModalVisible(true);
}}>
<Text style={{ color: 'white', fontSize: 15 }}> Update profil</Text>
</TouchableOpacity>
<View >
<View style={styles.main_container}>
<Text style={styles.text}>Nom:</Text>
<Text style={styles.text1}>{Data.nom}</Text>
</View>
.....
<View style={styles.main_container}>
<Text style={styles.text}>Ville:</Text>
<Text style={styles.text1}> {Data.ville}</Text>
</View>
</View>
</View>
);
}
I'm new to react native and I'll appreciate your help!

I'll assume you have the fetching details in componentDidMount of that profile page, and since modal also resides in it, so page doesnt refresh. What you can do is call that function again on modalClose.
suppose you have,
getDetails = () => {
.... fetch details
}
and in componentDidMount you call like :
componentDidmount(){
this.getDetails();
}
So same you can call on modalClose the same function after updating it.
onModalClose = () => {
this.getDetails()
}
hope its clear.feel free for doubtys

Related

How to use useState in other functions React Native

I have a question about react native. I want to write a username in the textInput here
function logIn({ navigation }) {
const [username, setUsername] = useState('');
return (
<View style={{ backgroundColor: "white"}} >
<View>
<TextInput style={styles.input} placeholder='username' placeholderTextColor='white' textAlign='center' onChangeText={(val) => setUsername(val)} />
</View>
<View>
<Button title="Go to Home" onPress={() => navigation.navigate('Home')} />
</View>
</View>
);
}
and then I want to see the username i wrote on this screen. Of course this doesnt work because it shows Cant find variable username so is there a way to fix this?
function HomeScreen({ navigation }) {
return(
<View style={{ flex:1, alignItems: 'center', justifyContent: 'center'}}>
<Text>Welcome {username}</Text>
</View>
);
}
You need to structure your state in a way that it is accessible to the components that need it. React likes you to have state in a component thats 'higher' in the tree than the components that need it, you can look at https://reactjs.org/docs/lifting-state-up.html for this.
Otherwise you could use the Context API or a 'global' store like Redux.
Logged in user data is a good candidate for the likes of Context or Redux, that way you will have access to the likes of username in any part of the application that is within your Context / Redux provider that provides the logged in data (or whatever other global data you may have)
You colud pass parameters to Home screen using navigation.navigate function in this way:
function logIn({ navigation }) {
const [username, setUsername] = useState('');
return (
<View style={{ backgroundColor: "white"}} >
<View>
<TextInput style={styles.input} placeholder='username' placeholderTextColor='white' textAlign='center' onChangeText={(val) => setUsername(val)} />
</View>
<View>
<Button title="Go to Home" onPress={() => navigation.navigate('Home', {username: username})} />
</View>
</View>
);
}
Then in HomeScreen:
function HomeScreen({ navigation, route }) {
return(
<View style={{ flex:1, alignItems: 'center', justifyContent: 'center'}}>
<Text>Welcome {route.params.username}</Text>
</View>
);
}

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>

too many re-renders when data moved to a separate component

Currently, I am using this logic to render data on the basis of results from a grapqhl query. This works fine:
const contacts = () => {
const { loading, error, data } = useUsersQuery({
variables: {
where: { id: 1 },
},
});
if (data) {
console.log('DATA COMING', data);
const contactName = data.users.nodes[0].userRelations[0].relatedUser.firstName
.concat(' ')
.concat(data.users.nodes[0].userRelations[0].relatedUser.lastName);
return (
<View style={styles.users}>
<View style={styles.item} key={data.users.nodes[0].id}>
<Thumbnail
style={styles.thumbnail}
source={{
uri:
'https://cdn4.iconfinder.com/data/icons/avatars-xmas-giveaway/128/girl_avatar_child_kid-512.png',
}}></Thumbnail>
<Text style={styles.userName}>{contactName}</Text>
</View>
</View>
);
}
};
return (
<SafeAreaView style={{ flex: 1 }}>
<Container style={{ flex: 1, alignItems: 'center' }}>
<Item style={styles.addToWhitelist}>
<Icon name="add" onPress={() => navigation.navigate('AddContact')} />
<Text style={styles.addToContactTitle}>Add contact</Text>
</Item>
<Text onPress={() => navigation.navigate('Home')}>Zurück</Text>
<View style={{ width: moderateScale(350) }}>
<Text>Keine Kontacte</Text>
</View>
{contacts()}
{/* <ContactList data={userData}></ContactList> */}
</Container>
</SafeAreaView>
);
};
However, when I make a separate component :
export const ContactList: React.FunctionComponent<UserProps> = ({
data,
}) => {
console.log('user called');
if (!data) return null;
console.log('DATA COMING', data);
const contactName = data.users.nodes[0].userRelations[0].relatedUser.firstName
.concat(' ')
.concat(data.users.nodes[0].userRelations[0].relatedUser.lastName);
return (
<View style={styles.users}>
<View style={styles.item} key={data.users.nodes[0].id}>
<Thumbnail
style={styles.thumbnail}
source={{
uri:
'https://cdn4.iconfinder.com/data/icons/avatars-xmas-giveaway/128/girl_avatar_child_kid-512.png',
}}></Thumbnail>
<Text style={styles.userName}>{contactName}</Text>
</View>
</View>
);
};
and call it like this:
const [userData, setUserData] = useState<UsersQueryHookResult>('');
const contacts = () => {
console.log('running');
const { loading, error, data } = useUsersQuery({
variables: {
where: { id: 1 },
},
});
if (data) {
setUserData(data);
}
};
return (
<SafeAreaView style={{ flex: 1 }}>
<Container style={{ flex: 1, alignItems: 'center' }}>
<Item style={styles.addToWhitelist}>
<Icon name="add" onPress={() => navigation.navigate('AddContact')} />
<Text style={styles.addToContactTitle}>Add contact</Text>
</Item>
<Text onPress={() => navigation.navigate('Home')}>Zurück</Text>
<View style={{ width: moderateScale(350) }}>
<Text>Keine Kontacte</Text>
</View>
{/* {contacts()} */}
<ContactList data={userData}></ContactList>
</Container>
</SafeAreaView>
);
};
However, this gives me a too many re-renders issue. What am I missing? Probably something basic. I also tried using useEffects but I can't run a graphql query hook inside it. That gives me an invalid hook call error.
It seems your running in an endless recursion.
If you call contacts in you render block it causes a setState (through setUserData) which renders, so contacts is called once again and so on till infinite (or till the error).

React Native Swiper ref element scrollTo does not work on first render

I am implementing an Introduction feature in my app. Where the screens show after splash screen. I used react-native-swiper component for guiding the user to step by step tutorial in my app.
Here is my Slider.tsx component code below.
type SliderProps = {
// swiperEl: RefObject<Swiper>;
// onIndexChanged: (index: number) => void;
index: number;
};
const Slider = ({ index }: SliderProps) => {
const swiperEl = useRef(null);
useEffect(() => {
swiperEl.current!.scrollTo(index);
}, [index, swiperEl]);
return (
<View style={styles.sliderContainer}>
<Swiper
scrollEnabled={false}
index={index}
ref={swiperEl}
style={styles.wrapper}
>
<View style={styles.slide1}>
<View style={[styles.circle, { backgroundColor: '#FF7F50' }]} />
</View>
<View style={styles.slide2}>
<View style={[styles.circle, { backgroundColor: '#FF6347' }]} />
</View>
<View style={styles.slide3}>
<View style={[styles.circle, { backgroundColor: '#FF4500' }]} />
</View>
</Swiper>
</View>
);
};
and here is my WelcomeScreenContainer.tsx
const WelcomeScreen = () => {
const [currentPage, setPage] = useState(0);
const navigation = useNavigation();
const handleNextClick = () => {
if (currentPage === 2) {
navigation.navigate('LoginScreen');
return;
}
setPage((prevPage) => prevPage + 1);
};
const handleSkipPress = () => navigation.navigate('LoginScreen');
console.log('Parent', currentPage);
return (
<View style={styles.parentContainer}>
<View style={styles.headerContainer}>
{/* <Text style={{ fontSize: 35 }}>WelcomeScreen</Text> */}
</View>
<Slider index={currentPage} />
<View style={{ flex: 1 }} />
<ButtonContainer
onNextPress={handleNextClick}
onSkipPress={handleSkipPress}
/>
</View>
);
};
Whenever I click the next button it should automatically slide the swiper to the next component or screen. However, on the first render when the user clicks the next button the swiperEl.current!.scrollTo(index); on the useEffect block of Slider.tsx does not work. But when the user clicks for the second time not it suddenly works.
I am a beginner in using React but I follow the documentation carefully on how to use the hooks of useRef and useEffect. Maybe I am missing something?
Appreciate it if someone could help.
Thanks
https://www.npmjs.com/package/react-native-swiper now supports scrollTo feature.
I havent seen this in official documentation but maybe i wasnt careful enough, Here is what worked for me in year 2022
...
const milestoneData = [1, 2, 3, 4, 5] //your data
const mileStoneSwiperRef = useRef(null);
const scrollMileStoneTo = (index) => {
mileStoneSwiperRef?.current?.scrollTo(index);
}
return (<View>
<View style={{padding: 10, backgroundColor: BaseColor.mainColor2}}>
<ScrollView showsHorizontalScrollIndicator={false} alwaysBounceHorizontal={true} horizontal={true} >
{milestoneData?.map((item, index) => <>
<Pressable onPress={() => scrollMileStoneTo(index)} style={{padding: 5, marginRight: 10, alignItems: 'center'}}>
<Image source={Images.math} style={[styles.contact, {width: 60, height: 60}]}/>
</Pressable></>)}
</ScrollView>
</View>
<Swiper
ref={mileStoneSwiperRef}
loop={false}
centeredSlides={false}
showsPagination={false}
bounces={true}
removeClippedSubviews={false}
>
{milestoneData?.map((item, index) => <View>
<View style={styles.groupHeadingViews}>
<Text fontResizeable bold style={styles.escrowGroupHeading}>{(`${tradeMethod} Name`).toUpperCase()}</Text>
<View style={styles.subTitleAndWordCount}>
<Text fontResizeable style={styles.escrowGroupSubHeading}>Briefly Name Your Country</Text>
<Text fontResizeable style={styles.escrowGroupSubHeading}>{milestoneData?.[index]?.name?.length}/30</Text>
</View>
</View>
</View>)}
</Swiper>
</View>)
...
The trick to get the scrollTo() an index done is the following method
const scrollMileStoneTo = (index) => {
mileStoneSwiperRef?.current?.scrollTo(index);
}

How to pass arguments within onclick of button in react native?

I am trying to pass a data (server response) as an argument of a button.Actually in my case there are certain type of workers (listing within cards ). If clicked on a worker it should be saved to db with the corresponding worker's id.Upon clicking on the card of workers there will be a popup showing for confirmation.So if clicked on yes button I'm taking the corresponding worker's id and perform another fetch request for saving it to my db.But this is not working I'm confused how to pass arguments within onclick property of a button and take that argument within fetch method.Following is my code.I'm pasting only a portion of my code below.
updated code
export default class FirstScreen extends Component {
constructor(){
super();
this.state = {
workers: [],
}
}
componentWillMount(){
fetch('http://192.168.1.3:3000/api/worker', {
method:'GET',
headers:{
Accept: 'application/json'
}
})
.then(response => response.json())
.then(responseData =>
this.setState({
workers:responseData
})
)
}
onPressYes = (worker_id) => {
fetch('http://192.168.1.3:3000/api/work_detail',{
method:'POST',
headers:{
Accept:'application/json'
},
body:JSON.stringify({
worker_id
})
})
}
render() {
return (
<View style={{flex:1}}>
<Header />
<ScrollView>
{this.state.workers.map((a, index)=>
<Container>
<CardSection>
<TouchableOpacity
onPress={() => this.popupDialog.show()}
>
<View style={{ maringTop: 10, marginLeft:120}}>
<Image
style={{ height: 100, width: 100 }}
source={{ uri: a.work_type == 'Carpenter' ? images[0].image : images[1].image}}
/>
<Text style={{marginLeft:20, fontSize:20}}>{a.work_type}</Text>
</View>
</TouchableOpacity>
</CardSection>
</Container>
<View style={styles.buttonContainer}>
<TouchableOpacity onPress={() =>console.log('Clicked')}>
<Button
backgroundColor="#FF4500"
title='View Status' />
</TouchableOpacity>
</View>
</ScrollView>
<PopupDialog
ref={popupDialog => {
this.popupDialog = popupDialog;
}}
dialogStyle={{ backgroundColor: "#FFFFFF", height: 180, width:300, borderWidth:1,padding:10}}
overlayBackgroundColor="#fff"
dismissOnTouchOutside={true}
>
<View style={styles.dialogContentView}>
<Text style={{fontSize:18, margingTop:10,color:"#000000"}}>Are you sure you want to submit?</Text>
<View style={{flexDirection:'row'}}>
<View style={styles.button_1}>
<Button
title="Yes"
color="#FF6633"
onPress={() => this.onPressYes(worker_id)}
/>
</View>
<View style={styles.button_1}>
<Button
title="No"
color="#FF6633"
onPress={() =>this._onPressNo() }
/>
</View>
</View>
</View>
</PopupDialog>
</View>
})
);
}
}
workers is the array I'm fetching from server.
Can you try replacing _onPressYes = (a.worker_id) with with _onPressYes = (worker_id) and then
body:JSON.stringify({
worker_id
})
Let me know if that helps.
What I usually do is return a function with the given parameter. I mean wrap a function in a function like the following:
onClickHandler = (value) => () => {
// do whathever you want.
};
<Component onClick={this.onClickHandler(yourValue)}/>
Because the problem in your code is that the function will be execute without calling the onClick event because when you pass the argument to a simple function you are already calling it.
I hope it can help you :)

Categories

Resources