React-Native how to not run function when starting the app? - react-native

I am currently learning how to create app using React Native and I am running into the issue of why is my app running the method when the app just started running?
I thought that I am only calling the function componentDidMount() in my button onPress ?
everything works fine as intended but I am just not sure why that's happening.
Thanks for your help!
import React from 'react';
import { StyleSheet, Text, View, TextInput, Button } from 'react-native';
export default class App extends React.Component {
constructor(props){
super(props)
this.state = {
isLoading: true,
text: ''
}
}
componentDidMount(summonerIGN){
console.log("This is in summonerIGN", summonerIGN)
return fetch('https://na1.api.riotgames.com/lol/summoner/v3/summoners/by-name/' + summonerIGN +'?api_key=<APIKey>')
.then((response) => response.json())
.then((responseJson) => {
console.log("This is in responseJson", responseJson)
console.log("This is the summoner ID: ", responseJson.id)
this.setState({
isLoading: false,
dataSource: responseJson,
summonerID: responseJson.id,
summonerName: responseJson.name,
})
})
.catch((error) => {
console.error(error)
})
}
render() {
return (
<View style={{padding: 10}}>
<TextInput
style={{height: 40}}
placeholder="Search For Summoner!"
onChangeText={(text) => this.setState({
text: text
})}
/>
<Button
onPress={() => {
console.log("This is in this.state.text", this.state.text)
this.componentDidMount(this.state.text)
}}
title="Search"
color="#841584"
/>
<Text style={{padding: 10, fontSize: 20}}>
Searching for summoner: {this.state.text}
</Text>
<Text>
The summpner ID: {this.state.summonerID}
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});

You can't just call componentDidMount().
It gets executed automatically once your component has been loaded.
Instead of writing logic in componentDidMount(), write a separate function and call that function.
componentDidMount() is a lifecycle method.
Lifecycle method gets called automatically based on components loads.
export default class App extends React.Component {
constructor(props){
super(props)
this.state = {
isLoading: true,
text: ''
}
}
callApi = (summonerIGN) => {
console.log("This is in summonerIGN", summonerIGN)
return fetch('https://na1.api.riotgames.com/lol/summoner/v3/summoners/by-name/' + summonerIGN +'?api_key=<APIKey>')
.then((response) => response.json())
.then((responseJson) => {
console.log("This is in responseJson", responseJson)
console.log("This is the summoner ID: ", responseJson.id)
this.setState({
isLoading: false,
dataSource: responseJson,
summonerID: responseJson.id,
summonerName: responseJson.name,
})
})
.catch((error) => {
console.error(error)
})
}
render() {
return (
<View style={{padding: 10}}>
<TextInput
style={{height: 40}}
placeholder="Search For Summoner!"
onChangeText={(text) => this.setState({
text: text
})}
/>
<Button
onPress={() => {
console.log("This is in this.state.text", this.state.text)
this.callApi(this.state.text)
}}
title="Search"
color="#841584"
/>
<Text style={{padding: 10, fontSize: 20}}>
Searching for summoner: {this.state.text}
</Text>
<Text>
The summpner ID: {this.state.summonerID}
</Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});

Related

Add a button "see more" in FlatList?

I use flatList to make a list of elements. I would like to show 15 elements and then add a button "see more" to show the next 15 etc.
I was about tu use this tutorial : https://aboutreact.com/react-native-flatlist-pagination-to-load-more-data-dynamically-infinite-list/
But I don't need to use fetch, I already have set up the data (state.listData) and in fact, I'm a little lost on how to adapt it...
I thought that maybe anyone could help me a little.
Thanks a lot
this.state = {
selectedId: '',
setSelectedId:'',
listData:''
}
};
renderItem = ({ item }) => {
const backgroundColor = item.id === this.selectedId ? "transparent" : "fff";
return (
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Item
item={item}
onPress={() => this.props.navigation.navigate('UpdateTripsForm')}
style={{ backgroundColor }}
/>
<Image source={require("../../assets/images/arrow.png")} style={{width: 15, height:15, justifyContent: 'center'}}/>
</View>
);
};
initListData = async () => {
let list = await getFlights(0);
if (list) {
this.setState({
listData: list
});
}
};
render() {
return (
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.listData}
renderItem={this.renderItem}
maxToRenderPerBatch={15}
keyExtractor={(item) => item.id}
extraData={this.selectedId}
/>
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.view2}>
<Text style={styles.textimg2}>
{i18n.t("tripsform.action.back")}
</Text>
</View>
<Image
source={require("../../assets/images/btn-background.png")}
style={styles.tripsimg2}
/>
</TouchableOpacity>
</SafeAreaView>
);
};
}
I just tried this thanks to #Pramod 's answer :
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.flightsListitem, style]}>
<Text style={styles.h4}>{item.id}</Text>
</TouchableOpacity>
);
export default class FlightsList extends Component {
constructor(props) {
super(props);
this.state = {
selectedId: '',
setSelectedId:'',
listData:'',
page:1,
perPage:2,
loadMoreVisible:true,
displayArray:[]
}
};
renderItem = ({ item }) => {
const backgroundColor = item.id === this.selectedId ? "transparent" : "fff";
return (
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Item
item={item}
onPress={() => this.props.navigation.navigate('UpdateTripsForm')}
style={{ backgroundColor }}
/>
<Image source={require("../../assets/images/arrow.png")} style={{width: 15, height:15, justifyContent: 'center'}}/>
</View>
);
};
initListData = async () => {
let list = await getFlights(0);
if (list) {
this.setState({
listData: list
});
}
};
componentDidMount(){
this.setNewData()
// console.log(tempArray)
}
setNewData(){
var tempArray=[]
if(this.state.listData.length == this.state.displayArray.length){
this.setState({
loadMoreVisible:false
})
}else{
for(var i=0; i<(this.state.page*this.state.perPage); i++){
tempArray.push(this.state.listData)
}
this.setState({
displayArray: tempArray,
loadMoreVisible:true
})
}
}
loadMore(){
this.setState({
page: this.state.page+1
},()=>{
this.setNewData()
})
}
async UNSAFE_componentWillMount() {
this.initListData();
}
render() {
return (
<ImageBackground
source={require("../../assets/images/background.jpg")}
style={styles.backgroundImage}
>
<Header
backgroundImage={require("../../assets/images/bg-header.png")}
backgroundImageStyle={{
resizeMode: "stretch",
}}
centerComponent={{
text: i18n.t("mytrips.title"),
style: styles.headerComponentStyle,
}}
containerStyle={[styles.headerContainerStyle, { marginBottom: 0 }]}
statusBarProps={{ barStyle: "light-content" }}
/>
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.displayArray}
renderItem={this.renderItem}
keyExtractor={(item) => item.id}
extraData={this.selectedId}
/>
{this.state.loadMoreVisible == true?
<Button style={{width:'100%', height:10, backgroundColor:'green', justifyContent:'center', alignItems:'center'}}
title = 'load more'
onPress={()=>{this.loadMore()}}>
</Button>:null}
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.view2}>
<Text style={styles.textimg2}>
{i18n.t("tripsform.action.back")}
</Text>
</View>
<Image
source={require("../../assets/images/btn-background.png")}
style={styles.tripsimg2}
/>
</TouchableOpacity>
</SafeAreaView>
</ImageBackground>
);
};
}
the flatlist is not displayed : I get :
You can user pagination method with per page limit so that you can have granular control
Load the array per page when component mount
On every click increase the per page and based on per page update data of your flat list
And also put a flag which will check when the data has ended which will help to hide the load more button when data ends
Working example: https://snack.expo.io/#msbot01/suspicious-orange
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
SafeAreaView,
SectionList,
Switch,
FlatList
} from 'react-native';
import Constants from 'expo-constants';
import Icon from 'react-native-vector-icons/FontAwesome';
import AwesomeIcon from 'react-native-vector-icons/FontAwesome';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
page:1,
perPage:2,
loadMoreVisible:true,
DATA: [{
id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
title: 'First Item',
},
{
id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
title: 'Second Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'Third Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'fourth Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'fifth Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29sd72',
title: 'sixth Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29dr72',
title: 'seventh Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d7w2',
title: 'Eight Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29ad72',
title: 'Nineth Item',
},
{
id: '58694a0f-3da1-471f-bd96-14557d1e29d72',
title: 'Tenth Item',
}],
displayArray:[]
}
}
componentDidMount(){
this.setNewData()
// console.log(tempArray)
}
setNewData(){
var tempArray=[]
if(this.state.DATA.length == this.state.displayArray.length){
this.setState({
loadMoreVisible:false
})
}else{
for(var i=0; i<(this.state.page*this.state.perPage); i++){
tempArray.push(this.state.DATA[i])
}
this.setState({
displayArray: tempArray,
loadMoreVisible:true
})
}
}
loadMore(){
this.setState({
page: this.state.page+1
},()=>{
this.setNewData()
})
}
render() {
return (
<View style={{ flex: 1 }}>
<FlatList
data={this.state.displayArray}
renderItem={({item})=>
<View style={{flexDirection:'row'}}>
<Text style={{fontSize:20}}>{item.title} </Text>
</View>
}
keyExtractor={item => item.id}
/>
{this.state.loadMoreVisible == true?
<View style={{width:'100%', height:10, backgroundColor:'green', justifyContent:'center', alignItems:'center'}} onClick={()=>{this.loadMore()}}>Load more</View>:null
}
</View>
);
}
}
Set data in state (already done ==> this.state.listData)
Set counter in state (initialize with 1)
Set 15 first elements in state (you can name it "renderedData" or something like that) and then increase cuonter to 1
Add a function that increases the "renderedData" by 15 items by increasing the counter by one
Add Footer component to the list which will call the function you created in stage 3
To take only 15( or 30/45/60 etc..) items from the list you can do something like this:
this.setState({ renderedItem: listData.slice(0, counter*15) })

Results do not update after a change of state

I have a problem, when I do a search, I get the data from my API, the first time I do a search, everything is fine, all the data is displayed. However, when I do a second search immediately, nothing is updated.
I put in console.log, and I see that I'm getting this data back, yet the display is not updated.
import React, { Component } from "react";
import { SafeAreaView, StyleSheet } from "react-native";
import Search from "./Component/Search";
export default class App extends Component {
render() {
return (
<SafeAreaView style={styles.container}>
<Search />
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
}
});
import React from "react";
import { View, TextInput, Button, FlatList, StyleSheet } from "react-native";
import LivreItem from "../Component/LivreItem";
class Search extends React.Component {
constructor(props) {
super(props);
this.inputValue = "";
this.state = { livres: [] };
}
searchBooks = async () => {
const key = "&key=XXXXXXXXXXXXXXXXXXXXXXX";
const url = "https://www.googleapis.com/books/v1/volumes?q=" + this.inputValue + key;
return fetch(url)
.then(response => response.json())
.catch(e => {
console.log("Une erreur s'est produite");
console.log(e);
});
};
getBooks = () => {
if (this.inputValue.length > 0) {
this.searchBooks()
.then(data => this.setState({ livres: data.items }))
.catch(reject => console.log(reject));
}
};
changeText = text => {
this.inputValue = text;
};
render() {
return (
<View style={styles.header_container}>
<View style={styles.sub_container}>
<TextInput
onChangeText={text => this.changeText(text)}
style={styles.input}
placeholder="Ex: Harry Potter"
/>
<Button
style={styles.button}
title="Rechercher"
onPress={() => this.getBooks()}
/>
</View>
<FlatList
style={styles.list}
data={this.state.livres}
keyExtractor={(item, index) => item + index}
renderItem={({ item }) => <LivreItem livre={item.volumeInfo} />}
/>
</View>
);
}
}
const styles = StyleSheet.create({
sub_container: {
justifyContent: "space-between",
flexDirection: "row",
marginTop: 30,
paddingRight: 10,
paddingLeft: 10
},
header_container: {
flex: 1,
flexDirection: "column",
padding: 10
},
input: {
borderRadius: 4,
borderWidth: 0.5,
borderColor: "#d6d7da",
width: 150,
paddingLeft: 5
},
button: {
borderRadius: 4
},
list: {
paddingLeft: 15,
paddingRight: 15
}
});
export default Search;
import React from "react";
import { View, StyleSheet, Image, Text } from "react-native";
class LivreItem extends React.Component {
constructor(props) {
super(props);
this.state = { livre: this.props.livre};
this.description =
this.state.livre.description === null || this.state.livre.description === undefined
? "Pas de description disponible"
: this.state.livre.description;
this.img = this.state.livre.imageLinks;
this.image =
this.img === undefined ||
this.img.smallThumbnail === undefined ||
this.img.smallThumbnail === null
? null
: this.state.livre.imageLinks.smallThumbnail;
}
render() {
return (
<View style={styles.content}>
<View>
<Image style={styles.image} source={{ uri: this.image }} />
<Image style={styles.image} source={this.image} />
</View>
<View style={styles.content_container}>
<Text style={styles.titre}>{this.state.livre.title}</Text>
<Text style={styles.description} numberOfLines={4}>
{this.description}
</Text>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
content: {
height: 125,
flexDirection: "row",
marginTop: 15
},
content_container: {
flexDirection: "column",
flexShrink: 1,
marginLeft: 10
},
image: {
width: 100,
height: 100
},
titre: {
fontWeight: "bold",
flexWrap: "wrap"
},
description: {
flexWrap: "wrap"
}
});
export default LivreItem;
Thanks.
Configure the prop extraData in Flatlist component ala:
<FlatList
extraData={this.state.livres}
/>
Pass a boolean value to the FlatList extraData.
<FlatList
extraData={this.state.refresh}
/>

React native router flux; slow to load back to previous component

I'm using react-native-router-flux in my Card component to to link to my playerPage component.
This is my Card component:
class Card extends Component {
state = {
visible: false,
currentUser: ''
}
componentDidMount() {
if(this.props.player !== undefined){
axios.get(`http://localhost:4000/reports?players=${this.props.player}&startDate=2019-03-20T03:10:43.990Z&sort=date`)
.then(response => {
console.log(response)
this.props.loadCards(response.data)
})
} else if(this.props.team !== undefined) {
axios.get(`http://localhost:4000/reports?team=${this.props.team}&startDate=2019-03-20T03:10:43.990Z&sort=date`)
.then(response => {
console.log(response)
this.props.loadCards(response.data)
})
} else if(this.props.league !== undefined) {
console.log('got here')
axios.get(`http://localhost:4000/reports?league=${this.props.league}&startDate=2019-03-20T03:10:43.990Z&sort=date`)
.then(response => {
console.log(response)
this.props.loadCards(response.data)
})
} else {
axios.get(`http://localhost:4000/reports?league=NBA&league=NFL&league=MLB&startDate=2019-03-20T03:10:43.990Z&sort=date`)
.then(response => {
this.props.loadCards(response.data)
})
Auth.currentAuthenticatedUser()
.then((data) => {
this.props.loadFilters(data.attributes.sub)
this.setState({currentUser: data})
})
}
}
render() {
let cardValues = this.props.search === null ? this.props.card : this.props.search
return (
<View >
{
cardValues != null ?
cardValues.map((v,i) => {
return(
<View key={i}>
<Collapse
>
<CollapseHeader
>
<View>
<Text>
<Icon
name={this.iconName(v.player.team.league.acronym)}
size={12}
color='black'/>{' '}
<Text
onPress={
()=> {
Actions.playerPage({
player: v.player._id
})
}
}
>{v.player.player_name} - </Text>
</Text>
<Text>
<View>
</View>
</View>
<View>
</View>
</CollapseHeader>
</Collapse>
</View>
)
})
: null
}
</View>
)
}
}
export default connect(mapStateToProps, { loadCards, countMore, loadFilters })(Card))
This is my playerPage component:
PlayerPage = (props) => {
return(
<View>
<Header
rounded
>
<View style={{flexDirection: 'row', flexWrap: 'wrap', right: '43%', top: '50%', paddingBottom: 900}}>
<Icon name='chevron-left' size={10} color='#006FFF' />
<NativeText
onPress={() => {Actions.fullApp()}}
style ={{color: '#006FFF', fontSize: 12, fontFamily: 'Montserrat-Regular', top: '900%' }}
>
Back
</NativeText>
</View>
</Header>
<Card
player={props.player}
team={props.team}
league={props.league}
/>
</View>
)
}
export default PlayerPage
When I link to playerPage I render a new version of Card on playerPage.
The data that is shown on playerPage is determined by the API call in the componentDidMount of the Card component.
I initially direct to playerPage with onPress={ ()=> {Actions.playerPage({player: v.player._id})}}
This loads fine.
When I direct a user back to the fullApp component, which has the Card component on it, the data loads, but much more slowly.
This is what it looks like:
https://streamable.com/ugc0b
Any ideas why it loads slowly? That's my issue.

Showing loading animation when clicking login button not working

I have the following code. I have two issues. First one, When I click the login button is shows the loading animation. But, it should toggle if the login process is success or fail. It's not working. Second one, If I do not add this line of code "this.toggleLoader = this.toggleLoader.bind(this);", the toggleLoader function show the error, this.setState is not a function. Please not that after log in successfully, the page navigate to new screen Home page. How can I toggle the loader before that ? If I call the function toggleLoader() after the if loop, not working.
import React, {Component} from 'react'
import {
Alert,
AsyncStorage,
Keyboard,
Text,
View,
TextInput,
TouchableHighlight, TouchableOpacity,
Image,
ActivityIndicator,
} from 'react-native'
import config from "../../../../config";
import styles from './style'
import {Icon} from "react-native-elements";
class Login extends Component {
constructor(props) {
super(props);
this.state = {
credentials: {
email: "",
password: "",
},
loading: false,
};
this.toggleLoader = this.toggleLoader.bind(this);
}
updateText(text, field) {
let newCredentials = Object.assign(this.state.credentials);
newCredentials[field] = text;
this.setState = ({
credentials: newCredentials
})
}
toggleLoader() {
this.setState({
loading: !this.state.loading
});
}
async login() {
Keyboard.dismiss();
let credentials = this.state.credentials;
if (this.state.credentials.email == '' || this.state.credentials.password == '') {
Alert.alert("Please fill all the fields.");
} else {
const that = this;
credentials.email = that.state.credentials.email.toLowerCase();
// start loading when all fields are fill
this.setState({ loading: !this.state.loading });
fetch(config.baseURL + 'mobileapi/get_token/?username=' + `${that.state.credentials.email}` + '&password=' + `${that.state.credentials.password}`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
credentials: credentials,
}),
})
.then((response) => response.json())
.then(responseJson => {
//stop loading after successful response
this.setState({ loading: !this.state.loading });
if (responseJson.confirmation === "success") {
// alert(JSON.stringify(responseJson.data));
AsyncStorage.setItem('USER_ID', responseJson.data.user_id);
AsyncStorage.setItem('USER_NAME', responseJson.data.user_name);
AsyncStorage.setItem('USER_TYPE', responseJson.data.user_type);
AsyncStorage.setItem('FIRST_NAME', responseJson.data.first_name);
AsyncStorage.setItem('LAST_NAME', responseJson.data.last_name);
AsyncStorage.setItem('EMAIL', responseJson.data.user_email);
AsyncStorage.setItem('AUTHENTICATION_TOKEN', responseJson.data.token);
setTimeout(() => {
this.props.navigation.navigate("Home")
}, 500);
} else {
setTimeout(() => {
//code to handle an error
throw new Error(responseJson.message);
}, 500);
}
})
.catch((err) => {
//stop loading
this.setState({ loading: !this.state.loading });
setTimeout(() => {
if (JSON.stringify(err.message) === JSON.stringify('Network request failed')) {
alert('Please check your internet connection or try again later');
} else {
alert(JSON.stringify(err.message));
}
}, 500);
})
}
}
render() {
const loginText = (this.state.loader) ? 'Loading' : 'Login';
return (
<View style={styles.container}>
<Image source={require('../../../../assets/images/icons/logo.png')}
style={{width: 99, height: 99, margin: 5,}}/>
<Text style={{fontSize: 20, margin: 20, color: "#0aa1e2"}}>Test App</Text>
<View style={styles.inputContainer}>
<Image style={styles.inputIcon}
source={require('../../../../assets/images/icons/username.png')}/>
<TextInput style={styles.inputs}
placeholder="Username"
keyboardType="email-address"
underlineColorAndroid='transparent'
onChangeText={text => {
this.updateText(text, 'email')
}} value={this.state.email}
autoCorrect={false}
autoCapitalize={"none"}
/>
</View>
<View style={styles.inputContainer}>
<Image style={styles.inputIcon}
source={require('../../../../assets/images/icons/password.png')}/>
<TextInput style={styles.inputs}
placeholder="Password"
secureTextEntry={true}
underlineColorAndroid='transparent'
onChangeText={text => {
this.updateText(text, 'password')
}}
value={this.state.password}
autoCorrect={false}
secureTextEntry/>
</View>
<TouchableHighlight style={[styles.buttonContainer, styles.loginButton]}
onPress={this.login.bind(this)} >
<View style={{justifyContent: 'center', flex: 1, flexDirection: 'row'}}>
{this.state.loading === false ?
<Icon name='login' type='entypo' size={16} color='white'/> :
<ActivityIndicator size="small" color="#ffffff"/>}
<Text style={styles.loginText}> {loginText} </Text>
</View>
</TouchableHighlight>
</View>
);
}
}
export default Login;
I have updated your login() method. Please try it. It may help you.
async login() {
Keyboard.dismiss();
let credentials = this.state.credentials;
if (this.state.credentials.email == '' || this.state.credentials.password == '') {
Alert.alert("Please fill all the fields.");
} else {
credentials.email = that.state.credentials.email.toLowerCase();
// start loading when all fields are fill
this.toggleLoader();
fetch(config.baseURL + 'mobileapi/get_token/?username=' + `${that.state.credentials.email}` + '&password=' + `${that.state.credentials.password}`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
credentials: credentials,
}),
})
.then((response) => response.json())
.then(responseJson => {
//stop loading after successful response
this.toggleLoader();
if (responseJson.confirmation === "success") {
AsyncStorage.setItem('USER_ID', responseJson.data.user_id);
AsyncStorage.setItem('USER_NAME', responseJson.data.user_name);
AsyncStorage.setItem('USER_TYPE', responseJson.data.user_email);
AsyncStorage.setItem('AUTHENTICATION_TOKEN', responseJson.data.token);
setTimeout(() => {
this.props.navigation.navigate("Home")
}, 500);
} else {
setTimeout(() => {
//code to handle an error
}, 500);
}
})
.catch((err) => {
//stop loading
this.toggleLoader();
setTimeout(() => {
if (JSON.stringify(err.message) === JSON.stringify('Network request failed')) {
alert('Please check your internet connection or try again later');
} else {
alert(JSON.stringify(err.message));
}
}, 500);
})
}
}
You have set email in TextInput like this.state.email. this should be this.state.credentials.email. same things sholud be follow for password. change onPress event of render() method like this:
render() {
const loginText = (this.state.loader) ? 'Loading' : 'Login';
return (
<View style={styles.container}>
<Image source={require('../../../../assets/images/icons/logo.png')}
style={{width: 99, height: 99, margin: 5,}}/>
<Text style={{fontSize: 20, margin: 20, color: "#0aa1e2"}}>Test App</Text>
<View style={styles.inputContainer}>
<Image style={styles.inputIcon}
source={require('../../../../assets/images/icons/username.png')}/>
<TextInput style={styles.inputs}
placeholder="Username"
keyboardType="email-address"
underlineColorAndroid='transparent'
onChangeText={text => {
this.updateText(text, 'email')
}}
value={this.state.credentials.email}
autoCorrect={false}
autoCapitalize={"none"}
/>
</View>
<View style={styles.inputContainer}>
<Image style={styles.inputIcon}
source={require('../../../../assets/images/icons/password.png')}/>
<TextInput style={styles.inputs}
placeholder="Password"
secureTextEntry={true}
underlineColorAndroid='transparent'
onChangeText={text => {
this.updateText(text, 'password')
}}
value={this.state.credentials.password}
autoCorrect={false}
secureTextEntry/>
</View>
<TouchableHighlight style={[styles.buttonContainer, styles.loginButton]}
onPress={this.login.bind(this)} >
<View style={{justifyContent: 'center', flex: 1, flexDirection: 'row'}}>
{this.state.loading === false ?
<Icon name='login' type='entypo' size={16} color='white'/> :
<ActivityIndicator size="small" color="#ffffff"/>}
<Text style={styles.loginText}> {loginText} </Text>
</View>
</TouchableHighlight>
</View>
);
}
TypeError: this.setState is not a function. This error is coming from updateText() method.you have added = during setState, which is throwing the error.
updateText(text, field) {
let newCredentials = Object.assign(this.state.credentials);
newCredentials[field] = text;
// setState should be done like this
this.setState({
credentials: newCredentials
})
}

Reactnative Issue

I have a very simple login screen where the user enters userid and password. when the user clicks on login button then flow moves to next screen if entered userid/password combination is correct.
on click of login button, I am calling an API with userid and password as input. API returns a count of matching records from DB in JSON format.If the count is >0 then login is successful.
Issue: I have to click login button twice. when I click on login button nothing happens but if I click it again then I get fail message or move to the second screen depending on userid/password combination. I am copying my code below. I'll appreciate any help.
import React, { Component } from 'react';
import {
AppRegistry,
View,
Image,
StyleSheet,
KeyboardAvoidingView,
TextInput,
TouchableOpacity,
Text,
StatusBar,
Alert
} from 'react-native';
import {
StackNavigator
} from 'react-navigation';
export default class Login extends Component {
constructor(props) {
super(props)
this.state = {
email: '',
password: '',
uCount: -1,
data: []
};
}
getData(){
var url="https://myurl/verifySubscription.php?email=" + this.state.email + "&password=" + this.state.password;
console.log("URL:", url);
return fetch(url)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
uCount: responseJson.count
})
})
.catch((error) => {
console.error(error);
});
}
async _onPressButton() {
await this.getData();
console.log("uCount:", this.state.uCount);
if (this.state.uCount < 1) {
Alert.alert('Login Failed: Incorrect email or password')
} else {
this.props.navigation.navigate('LoginSuccess', { email: this.state.email, password: this.state.password})
}
}
render() {
return (
<KeyboardAvoidingView behavior="padding" style={styles.wrapper}>
<View style={styles.topView}>
<Image style={styles.imageStyle}
source={require('../images/main.jpg')}
/>
</View>
<View style={styles.bottomView}>
<StatusBar
barStyle="light-content"
/>
<TextInput style={styles.Input}
placeholder="Email"
placeholderTextColor="rgba(255,255,255,0.7)"
keyBoardType='email-address'
returnKeyType="next"
autoCapitalize="none"
autoCorrect={false}
onSubmitEditing={() => this.passwordInput.focus()}
onChangeText={(text) => this.setState({email:text})}
/>
<TextInput style={styles.Input}
placeholder="Password"
placeholderTextColor="rgba(255,255,255,0.7)"
returnKeyType="go"
secureTextEntry
autoCapitalize="none"
autoCorrect={false}
ref={(next) => this.passwordInput = next}
onChangeText={(text) => this.setState({password:text})}
/>
<TouchableOpacity style={styles.button1Container} onPress={ this._onPressButton.bind(this) }>
<Text style={styles.buttonText}>
Login
</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button2Container}>
<Text style={styles.buttonText}>
Sign up
</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
);
}
}
const styles = StyleSheet.create({
wrapper: {
backgroundColor: '#4A7AA5',
flex: 1
},
topView: {
flexGrow: 1
},
imageStyle: {
width: null,
flex: 1
},
bottomView: {
padding: 20
},
Input: {
height:40,
backgroundColor: 'rgba(255,255,255,0.3)',
marginBottom: 10,
color: '#FFF',
paddingHorizontal: 10
},
button1Container: {
backgroundColor: 'rgba(200,200,255,0.3)',
padding: 10
},
buttonText: {
textAlign: 'center',
color: '#FFF',
fontWeight: '700'
},
button2Container: {
padding: 10
}
});
Issue maybe related to the async process when you press the button. fetch will return a promise which then can be resolve after response is completed. Try this, not using await (es2017), just es6.
getData(){
var url="https://myurl/verifySubscription.php?email=" + this.state.email +
"&password=" + this.state.password;
console.log("URL:", url);
return fetch(url);
}
_onPressButton() {
this.getData().then((response) => response.json())
.then((responseJson) => {
const cnt = responseJson.cnt;
if (cnt < 1) {
Alert.alert('Login Failed: Incorrect email or password')
} else {
this.props.navigation.navigate('LoginSuccess', { email:
this.state.email, password: this.state.password})
}
});
}