How to pass data from screen to screen from flat list item - react-native

How do i able to pass the list items from first screen to the third screen, i am able to pass data on to the second screen but not the third screen. What i'm trying to achieve is how do i able to edit the list item on the third screen and inside the edit text input get the data and update. By the way, i'm using flat list item, because list view is deprecated. Below are my codes and screenshots.
First Screenshot
Second screenshot
Third screenshot
First screenshot coding
class SecondScreen extends Component {
constructor(props){
super(props);
this.state = {
loading: true,
email: '',
name: '',
title: '',
description: '',
error: '',
dataSource: [],
isFetching: false
}
}
onPress(item){
this.props.navigation.navigate(
'DetailsScreen',
{item},
);
}
renderItem = ({ item }) => {
return (
<TouchableOpacity style={{ flex: 1, flexDirection: 'row',
marginBottom: 3}}
onPress={() => { this.onPress(item) }}>
<View style={{ flex: 1, justifyContent: 'center', marginLeft:
5}}>
<Text style={{ fontSize: 18, color: 'green', marginBottom:
15}}>
{"ID - "+item.id}
</Text>
<Text style={{ fontSize: 18, color: 'green', marginBottom:
15}}>
{"Title - "+item.title}
</Text>
<Text style={{ fontSize: 16, color: 'red'}}>
{"Description - "+item.description}
</Text>
</View>
</TouchableOpacity>
)
}
renderSeparator = () => {
return (
<View
style={{height: 1, width: '100%', backgroundColor: 'black'}}>
</View>
)
}
ListEmptyView = () => {
return (
<View style={styles.container}>
<Text style={{textAlign: 'center'}}> No job available.</Text>
</View>
);
}
handleBackButton = () => {
Alert.alert(
'Exit App',
'Exiting the application?', [{
text: 'Cancel',
onPress: () => console.log('Cancel Pressed'),
style: 'cancel'
}, {
text: 'OK',
onPress: () => BackHandler.exitApp()
},],{
cancelable: false
}
)
return true;
}
componentDidMount(){
this.getAvailableJob()
BackHandler.addEventListener('hardwareBackPress',
this.handleBackButton);
}
getAvailableJob() {
const headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + this.props.token
};
axios({
method: 'GET',
url: 'http://192.168.1.201:8000/api/jobs',
headers: headers,
}).then((response) => {
console.log('response3',response)
console.log('response4',this.props.token)
this.setState({
dataSource: response.data,
isFetching: false
});
}).catch((error) => {
console.log(error);
this.setState({
error: 'Error retrieving data',
loading: false
});
});
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress',
this.handleBackButton);
}
onRefresh() {
this.setState({ isFetching: true }, function() { this.getAvailableJob()
});
}
render() {
const { container, emailText, errorText } = styles;
const { loading, email, name, error, title, description } = this.state;
return(
<View style={container}>
<FlatList
data={this.state.dataSource}
onRefresh={() => this.onRefresh()}
refreshing={this.state.isFetching}
renderItem={this.renderItem}
keyExtractor={(item, index) => index.toString()}
ListEmptyComponent={this.ListEmptyView}
ItemSeparatorComponent={this.renderSeparator}
/>
</View>
);
}
}
export default withNavigation(SecondScreen);
Second screenshot coding
class DetailsScreen extends React.Component {
handleBackButton = () => {
this.props.navigation.popToTop();
return true;
}
componentDidMount(){
BackHandler.addEventListener('hardwareBackPress',
this.handleBackButton);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress',
this.handleBackButton);
}
onPress(item){
this.props.navigation.push(
'EditDetailsScreen',
{item},
);
}
render() {
const { backButton } = styles;
let item = this.props.navigation.state.params.item;
return (
<View style={styles.container}>
<View style={[styles.container2, { backgroundColor: 'yellow' },
styles.hiddenContainer]}>
<Text style = { styles.TextStyle }> ID {
this.props.navigation.state.params.item.id }</Text>
</View>
<Text style = { styles.TextStyle }> Title {
this.props.navigation.state.params.item.title }</Text>
<Text style = { styles.TextStyle }> Description {
this.props.navigation.state.params.item.description }</Text>
<TouchableOpacity
style = {styles.submitButton}
onPress = {() => { this.onPress(item) }}>
<Text style = {styles.submitButtonText}> Edit </Text>
</TouchableOpacity>
</View>
);
}
}
export default withNavigation(DetailsScreen);
Third screenshot coding
class EditDetailsScreen extends React.Component {
handleTitle = (text) => {
this.setState({ title: text })
}
handleDescription = (text) => {
this.setState({ description: text })
}
handleBackButton = () => {
this.props.navigation.popToTop();
return true;
}
componentDidMount(){
BackHandler.addEventListener('hardwareBackPress',
this.handleBackButton);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress',
this.handleBackButton);
}
updateJobDetails = () => {
const headers = {
'Accept': 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ' + this.props.token
};
axios({
method: 'PUT',
url: 'http://192.168.1.201:8000/api/jobs',
headers: headers,
}).then((response) => {
this.setState({
title: response.data,
description: response.data,
loading: false
});
}).catch((error) => {
console.log(error);
this.setState({
error: 'Error retrieving data',
loading: false
});
});
}
render() {
return (
<View style={styles.container}>
<View style={[styles.container2, { backgroundColor: 'yellow' },
styles.hiddenContainer]}>
<Text style = { styles.TextStyle }> ID {
this.props.navigation.state.params.item.id }</Text>
</View>
<Text style = { styles.TextStyle }> Title {
this.props.navigation.state.params.item.title }</Text>
<Text style = { styles.TextStyle }> Description {
this.props.navigation.state.params.item.description }</Text>
<TextInput style = {styles.input}
underlineColorAndroid = "transparent"
placeholder = "Edit Title"
placeholderTextColor = "#9a73ef"
autoCapitalize = "none"
onChangeText = {this.handleTitle}/>
<TextInput style = {styles.input}
underlineColorAndroid = "transparent"
placeholder = "Edit Description"
placeholderTextColor = "#9a73ef"
autoCapitalize = "none"
onChangeText = {this.handleDescription}/>
<TouchableOpacity
style = {styles.submitButton}
onPress = {this.updateJobDetails}>
<Text style = {styles.submitButtonText}> Submit </Text>
</TouchableOpacity>
</View>
);
}
}
export default withNavigation(EditDetailsScreen);
I can update using postman but not in react native, and also in postman i had to set the Body to use x-www-form-urlencoded instead of form-data. So how do i able to update in react native? Any help would be much appreciated. Below is my postman screenshot.
Postman

A suggestion is to use a state container like react-redux, but if you don't want: you can pass a function in the props like
<renderedItem update={(newVal) => this.setState({ val: newVal})}>
and then in the renderedItem you can call this function with new value like so:
this.props.update(this.state.newVal)

I've solved the problem by using qs.stringify(data). Below is my updated code.
updateJobDetails = () => {
url = 'http://192.168.1.201:8000/api/jobs/' + this.props.navigation.state.params.item.id;
const qs = require('qs');
const data = {title:this.state.TextInput_Title,
description:this.state.TextInput_Description};
const options = {
method: 'PUT',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Bearer ' + this.props.token
},
data: qs.stringify(data),
url
}
axios(options).then((response) => {
console.log('update response',response)
Alert.alert(
'Update successful',
'Your details has been updated', [{
text: 'OK',
onPress: () => this.props.navigation.popToTop()
},]
,{
cancelable: false
}
)
this.setState({
loading: false,
});
}).catch((error) => {
Alert.alert("Please Enter All the Values.");
console.log(error);
this.setState({
error: 'Error retrieving data',
loading: false
});
});
}
render() {
return (
<View style={styles.container}>
<View style={[styles.container2, { backgroundColor: 'yellow' },
styles.hiddenContainer]}>
<Text style = { styles.TextStyle }> ID {
this.props.navigation.state.params.item.id }</Text>
</View>
<Text style = {styles.TextStyle2}>
Title:
</Text>
<TextInput style = {styles.input}
underlineColorAndroid = "transparent"
placeholder = "Edit Title"
placeholderTextColor = "#9a73ef"
autoCapitalize = "none"
value={this.state.TextInput_Title}
onChangeText = { TextInputValue => this.setState({TextInput_Title: TextInputValue})}/>
<Text style = {styles.TextStyle2}>
Description:
</Text>
<TextInput style = {styles.input}
underlineColorAndroid = "transparent"
placeholder = "Edit Description"
placeholderTextColor = "#9a73ef"
autoCapitalize = "none"
value={this.state.TextInput_Description}
onChangeText = { TextInputValue =>
this.setState({TextInput_Description: TextInputValue})}/>
<TouchableOpacity
style = {styles.submitButton}
onPress = {this.updateJobDetails}>
<Text style = {styles.submitButtonText}> Submit </Text>
</TouchableOpacity>
</View>
);
}

Related

Searching in Expo's FlatList

I used this tutorial but it didn't work (if you are interested, check out my last post). Any suggestions to make a working search for a flatlist?
I have a list of 100 things and just by inserting the name in a search bar, the flatlist should update with the results.
Try using react-native-searchable-flatlist
import React, { Component } from 'react';
import { View, Text, FlatList, ActivityIndicator } from 'react-native';
import { ListItem, SearchBar } from 'react-native-elements';
class FlatListDemo extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
error: null,
};
this.arrayholder = [];
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const url = `https://randomuser.me/api/?&results=20`;
this.setState({ loading: true });
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: res.results,
error: res.error || null,
loading: false,
});
this.arrayholder = res.results;
})
.catch(error => {
this.setState({ error, loading: false });
});
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: '86%',
backgroundColor: '#CED0CE',
marginLeft: '14%',
}}
/>
);
};
searchFilterFunction = text => {
this.setState({
value: text,
});
const newData = this.arrayholder.filter(item => {
const itemData = `${item.name.title.toUpperCase()} ${item.name.first.toUpperCase()} ${item.name.last.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
this.setState({
data: newData,
});
};
renderHeader = () => {
return (
<SearchBar
placeholder="Type Here..."
lightTheme
round
onChangeText={text => this.searchFilterFunction(text)}
autoCorrect={false}
value={this.state.value}
/>
);
};
render() {
if (this.state.loading) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{ flex: 1 }}>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem
leftAvatar={{ source: { uri: item.picture.thumbnail } }}
title={`${item.name.first} ${item.name.last}`}
subtitle={item.email}
/>
)}
keyExtractor={item => item.email}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
/>
</View>
);
}
}
export default FlatListDemo;

using classes and rendering api call in React Native

I am totally new in React Native, I am having problem rendering data from API call. When I do it inside function it is working for me when I am using useEffect... but in the Class I cannot use that.
Here is example of my code...
export default class Categories extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
error: null,
};
}
componentDidMount() {
this.regionsGetRequest();
}
regionsGetRequest = () => {
this.setState({ loading: true });
const fetchData = async () => {
const result = await axios(
'https://...........json'
);
this.setState({
data: result.data,
error: result.error || null,
loading: false,
});
};
fetchData();
};
renderCategories = () => {
const categoryLive = this.state.data;
console.log(JSON.stringify(categoryLive));
I am getting in console: undefined, undefined... and then results as they should... like it is running 3 times for some reason... if I try to put above renderCategories:
componentDidMount() {
renderCategories();
}
I am getting just one undefined... when I connect variable categoryLive nothing is loading.
Sorry I have been strugling with this one... any help is really appreciated!!
No matter what I do, I am always getting 3 calls... first two empty object [], and third I get real results dumped in console. So my categories are not rendering.. they are empty.
Here is updated code, and I am posting whole file, it might ring some bells.
export default class Categories extends React.Component {
state = {
myData: [],
};
componentDidMount() {
axios
.get('https://..............json')
.then((res) => {
const myData = res.data;
this.setState({ myData });
});
}
renderCategories = () => {
const categoryLive = this.state.myData;
console.log(JSON.stringify(categoryLive));
const { navigation, route } = this.props;
const tabId = route.params?.tabId;
const categories = tabId
? categoryLive[tabId]
: categoryLive.victoria_bc;
//console.log(route.params?.tabId);
return (
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.categoryList}
>
<Block flex>
{categories.map((category) => (
<TouchableWithoutFeedback
key={`category-${category.id}`}
onPress={() => navigation.navigate('Category', { ...category })}
>
<Block flex card style={[styles.category, styles.shadow]}>
<ImageBackground
source={{ uri: category.image }}
style={[
styles.imageBlock,
{ width: width - theme.SIZES.BASE * 2, height: 252 },
]}
imageStyle={{
width: width - theme.SIZES.BASE * 2,
height: 252,
}}
>
<Block style={styles.categoryTitle}>
<Text size={18} bold color={theme.COLORS.WHITE}>
{category.title}
</Text>
</Block>
</ImageBackground>
</Block>
</TouchableWithoutFeedback>
))}
</Block>
</ScrollView>
);
};
render() {
return (
<Block flex center style={styles.categories}>
{this.renderCategories()}
</Block>
);
}
}
When I put it like this: I am getting default category ( and all data just fine... ) but my navigation is not working any more... (route.params?.tabId is not updating)
axios
.get('https://.............json')
.then((res) => {
this.setState({
myData: res.data,
error: res.error || null,
loading: false,
});
console.log('inside .then----' + JSON.stringify(this.state.myData));
const { navigation, route } = this.props;
const tabId = route.params?.tabId;
const tmpCategories = tabId
? this.state.myData[tabId]
: this.state.myData.victoria_bc;
this.setState({ categories: tmpCategories });
//console.log(route.params?.tabId);
});
If I put it like this as below... category is empty for me:
axios
.get('https://.............json')
.then((res) => {
this.setState({
myData: res.data,
error: res.error || null,
loading: false,
});
console.log('inside .then----' + JSON.stringify(this.state.myData));
});
const { navigation, route } = this.props;
const tabId = route.params?.tabId;
const tmpCategories = tabId
? this.state.myData[tabId]
: this.state.myData.victoria_bc;
this.setState({ categories: tmpCategories });
//console.log(route.params?.tabId);
Final code that is working for me..
export default class Categories extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true,
myData: [],
error: null,
};
}
componentDidMount() {
this.renderCategories();
}
renderCategories = () => {
axios
.get('https://.............json')
.then((res) => {
this.setState({
myData: res.data,
error: res.error || null,
loading: false,
});
//console.log('inside .then----' + JSON.stringify(this.state.myData));
});
};
render() {
if (this.state.loading) {
return (
<View
style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}
>
<ActivityIndicator />
</View>
);
} else {
const { navigation, route } = this.props;
const tabId = route.params?.tabId;
const categories = tabId
? this.state.myData[tabId]
: this.state.myData.victoria_bc;
//console.log(route.params?.tabId);
return (
<Block flex center style={styles.categories}>
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.categoryList}
>
<Block flex>
{categories.map((category) => (
<TouchableWithoutFeedback
key={`category-${category.id}`}
onPress={() =>
navigation.navigate('Category', { ...category })
}
>
<Block flex card style={[styles.category, styles.shadow]}>
<ImageBackground
source={{ uri: category.image }}
style={[
styles.imageBlock,
{ width: width - theme.SIZES.BASE * 2, height: 252 },
]}
imageStyle={{
width: width - theme.SIZES.BASE * 2,
height: 252,
}}
>
<Block style={styles.categoryTitle}>
<Text size={18} bold color={theme.COLORS.WHITE}>
{category.title}
</Text>
</Block>
</ImageBackground>
</Block>
</TouchableWithoutFeedback>
))}
</Block>
</ScrollView>
</Block>
);
}
}
}
export default class Categories extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false,
myData: [],
error: null,
categories: []
};
}
componentDidMount() {
renderCategories();
}
renderCategories = () => {
this.setState({ loading: true });
axios
.get('https://..............json')
.then((res) => {
this.setState({ myData: res.data,
error: res.error || null,
loading: false
});
const categoryLive = this.state.myData;
console.log(JSON.stringify(categoryLive));
});
};
render() {
// Set params in your render method :
const { navigation, route } = this.props;
const tabId = route.params?.tabId;
if (this.state.loading){
return (
<View style={{ flex : 1, alignItems: 'center', justifyContent: 'center' }}>
<ActivityIndicator/>
</View>
);
}
return (
// assign in your return statement
this.setState({ categories: tabId
? categoryLive[tabId]
: categoryLive.victoria_bc;})
//console.log(route.params?.tabId);
const { categories } = this.state.categories;
<Block flex center style={styles.categories}>
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.categoryList}
>
<Block flex>
{categories.map((category) => (
<TouchableWithoutFeedback
key={`category-${category.id}`}
onPress={() => navigation.navigate('Category', { ...category })}
>
<Block flex card style={[styles.category, styles.shadow]}>
<ImageBackground
source={{ uri: category.image }}
style={[
styles.imageBlock,
{ width: width - theme.SIZES.BASE * 2, height: 252 },
]}
imageStyle={{
width: width - theme.SIZES.BASE * 2,
height: 252,
}}
>
<Block style={styles.categoryTitle}>
<Text size={18} bold color={theme.COLORS.WHITE}>
{category.title}
</Text>
</Block>
</ImageBackground>
</Block>
</TouchableWithoutFeedback>
))}
</Block>
</ScrollView>
</Block>
);
}
}

How to refresh a single item in a FlatList in React-Native?

I made a Like function with React and Flask API, it works pretty well, it makes the Like action but it only appears when I refresh the whole list. Somehow I want to refresh the Like picture and the Like count at the post.
I tried to put extraData in my FlatList but that does not solve my problem...
handleClick() {
const headers = {
'Authorization': 'jwt ' + this.props.jwt
};
const action = this.props.has_liked? 'unlike':'like'
axios.post("http://127.0.0.1:5000/API/likeaction",{
id: this.props.id,
action: action
}, {
headers: headers
})
.then((response) => {
console.log(response.data);
})
.catch((error) => {
console.log(error)
});
this.setState({liked: action})
}
render() {
const img = this.props.has_liked? require('../assets/icons/heart-filled.png') : require('../assets/icons/heart-no-fill.png')
return(
<View style={{flex:1, marginTop: 10, marginBottom:16, left: 20}}>
<View style={{flex: 1, flexDirection: 'row'}}>
<Image source={this.props.image_uri} style={{height:42, width: 42, borderRadius:21}}/>
<Text style={{left:20,paddingTop:6}}>{this.props.author}</Text>
<Text style={{position: 'absolute', right: 40, paddingTop:6,fontSize:12,color:'#babbbc'}}> {this.props.date} </Text>
</View>
<View style={{flex:1, left: 60, right:20,width: '70%', marginTop:-10}}>
<Text style={{fontWeight:'bold',fontSize:18,marginBottom:6}}>{this.props.title} </Text>
<Text style={{fontSize:16,marginBottom:6 }}>{this.props.content}</Text>
<View style={{flex: 1, flexDirection: 'row'}}>
<TouchableOpacity onPress={this.handleClick}>
<Image style={{width:24, height:24, marginBottom:6, marginTop:6}} source={img} />
</TouchableOpacity>
<Text style={{paddingTop:10, marginLeft:6, fontSize:14,color:'#bfbfbf'}}>{this.props.likes}</Text>
</View>
</View>
</View>
);
}
}
<FlatList
data={this.state.dataSource}
extraData={this.state}
renderItem={({item}) => <PostView title={item.title}
content={item.content}
author={item.author}
date={item.date_posted}
likes={item.likes}
has_liked={item.has_liked}
jwt = {this.props.screenProps.jwt}
id = {item.id}
image_uri={{uri:'http://127.0.0.1:5000/static/profile_pics/'+item.profile_image}}/> }
refreshing={this.state.refreshing}
onRefresh={this.handleRefresh}
keyExtractor={item => item.id}/>
</View>
extraData :
extraData={{this.state}}
and put this.setState({liked: action}) here:
.then((response) => {
console.log(response.data);
this.setState({liked: action})
})
try with in home
this.state = {
loading: true,
email: '',
error: '',
refreshing: false,
dataSource: [],
updated: false
}
handleChange = () => {
this.setState({ updated: true })
}
handleRefresh = () => {
this.setState({
refreshing: true,
data: this.state.dataSource
}, () => {
this.makeRemoteRequest()
})
}
Replace
<PostView title={item.title}
....
/>
with
<PostView title={item.title}
.....
handleChange={this.handleChange}/>
extraData={this.state}
and update
and
handleClick() {
const headers = {
'Authorization': 'jwt ' + this.props.jwt
};
axios.post("http://127.0.0.1:5000/API/likeaction",{
id: this.props.id,
}, {
headers: headers
})
.then((response) => {
console.log(response.data);
if(response.data.account == "liked"){
this.setState({liked:true})
}else{
this.setState({liked:false})
}
})
.catch((error) => {
console.log(error)
});
}
with
handleClick() {
const headers = {
'Authorization': 'jwt ' + this.props.jwt
};
axios.post("http://127.0.0.1:5000/API/likeaction",{
id: this.props.id,
}, {
headers: headers
})
.then((response) => {
console.log(response.data);
this.props.handleChange(true);
if(response.data.account == "liked"){
this.setState({liked:true})
}else{
this.setState({liked:false})
}
})
.catch((error) => {
console.log(error)
});
}
const img = this.state.liked? require('../assets/icons/heart-filled.png') : require('../assets/icons/heart-no-fill.png')
with
const img = this.state.liked ? require('../assets/icons/heart-filled.png') : require('../assets/icons/heart-no-fill.png')

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
})
}

React-Native : how show loading screen before list view populated

Here is my code .I am using ComponentwillMount Function for API and then using isLoading variable and accordingly displaying the particular view.But its doesnt work... its seems to be I done something wrong please took a glance and help me out I am new to react - native
const UserPage = React.createClass({
getInitialState() {
return {
dataSource: dataSource.cloneWithRows(dataList),
isLoading:true
}
},
componentWillMount() {
SharedPreferences.getItem("sender_id", (value) => {
let user_id = value;
let url = "BaseURL/fetchSendTokens?user_id="+user_id;
fetch(url, {
method: "GET",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
})
.then((response) => response.json())
.then((responseData) => {
if (responseData.responsecode === 1) {
dataList = responseData.data;
this.setState = ({
dataSource: this.state.dataSource.cloneWithRows(dataList),
isLoading:false,
})
}
else {
ToastAndroid.show('Please try again...', ToastAndroid.SHORT);
}
})
.catch(e => {
ToastAndroid.show('Please try again...', ToastAndroid.SHORT);
})
.done();
});
},
renderRow(rowData, sectionID,){
let currentView = (rowData.visitor_profileImageUrl)?
<Image
source={{uri:rowData.visitor_profileImageUrl}}
style={{height:40,width:40,margin:15,borderRadius:100}}
resizeMode="contain"/> :
<Image
source={userprofilepic}
style={{height:40,width:40,margin:15,borderRadius:100}}
resizeMode="contain"/>;
return <View key={ sectionID }>
<View style={{ flex: 1,height:70,flexDirection: 'row'}}>
<View>
{currentView}
</View>
<Text style={{fontSize : 18,marginTop:15,marginLeft:20,color:"black"}} >{ rowData.visitor_name }</Text>
</View>
</View>
},
render() {
let loadingGif = "URL-loading.gif";
let currentView = (this.state.isLoading)?<View style={styles.background}>
<View style={styles.markWrap}>
<Image source={{uri:loadingGif}} style={styles.verifyLogo} resizeMode="contain"/>
</View>
</View>:
<ListView dataSource={this.state.dataSource} renderRow={this.renderRow} enableEmptySections={true}/>;
return(
<View>
{currentView}
</View>
);
},
});
Your problem is a typo:
this.setState = ({
datasource: this.state.dataSource.cloneWithRows(dataList),
isLoading:false,
})
Should be:
this.setState = ({
dataSource: this.state.dataSource.cloneWithRows(dataList),
isLoading:false,
})
Note that you seem to keep around a global dataSource and dataList - that's usually not a good practice. If you need to keep data, use props and callbacks to pass it around, not globals.
constructor(props){
super(props);
this.state = {
loading: true,
}
}
render(){
if (this.props.loading) {
return (<View style={{ flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}><ActivityIndicator
animating={states.campaign.loading}
style={[styles.centering, { height: 80 }]}
size="large"
/></View>);
}else{
return (<ListView></ListView>);
}
}