maximum call stack size exceed - react-native

Guys i have a main component like this
export default class mobile extends Component {
constructor(props){
super(props);
this.state = {
loading: true
}
}
async componentWillMount(){
let defaultKey = 'login';
const token = await AsyncStorage.getItem('token');
if (token){
console.log('token');
const storedUser = await AsyncStorage.getItem('user');
const payload = JSON.parse(storedUser);
store.dispatch({
type: 'LOGIN_SUCCESS',
payload
})
defaultKey = 'home';
}
const config = await AsyncStorage.getItem('config');
console.log(config);
if ((process.env.NODE_ENV === "development") && !config) {
defaultKey = 'setup';
}
this.setState({loading: false});
Actions[defaultKey]();
}
render() {
return(
<Provider store={ store }>
<Router>
<Scene key="root">
<Scene key="setup"
title="Configuration"
component={ ConfigComponent } />
<Scene key="login"
title="Login"
component={ Login } />
<Scene key="home"
title="Home"
component={ Home } />
<Scene key="signup"
title="Signup"
component={ Signup }/>
</Scene>
</Router>
</Provider>
)
}
};
and another config component which basically takes TextInput value and after successful submission it navigates to login page with
Action.login() where login is key name of login scene
I am not sure when i call Action.login() after successful submission inside fetch post success callback.it throws me error "maximum call size exceeded".My simulator freezes and after many seconds i can see my login view.I tried to call Action.login() outside of fetch call, but same issue. Please look at handleFormSubmit() function.
import React, { Component } from 'react';
import update from 'react-addons-update';
import { View, Text, TextInput, Button, ScrollView, TouchableHighlight, Platform } from 'react-native';
import { Actions, ActionConst } from 'react-native-router-flux';
import { ProgressIndicator } from '../common-scenes/progress';
import { Styles } from '../../app/common/styles';
import { AUTH } from '../../app/common/enums';
import { alert } from '../../app/common/alert';
import { asyncStorage } from '../../app/common/helper';
import { InputBox } from './inputComponent';
export default class ConfigComponent extends Component{
constructor(){
super();
this.state = {
formObject: {
APIURL: 'http://localhost:3000',
MongodbURL: 'mongodb://localhost:27017/react_app',
FacebookAppID: '1780990305515917',
FacebookClientSecret: 'cc7341b671731df7efe44add63b1c79e',
FacebookDisplayName: 'Wish',
Platform: Platform.OS
},
errors:{
APIError: '',
FacebookError: '',
MongodbError: ''
},
test:{
API: false,
Facebook: false,
MongoDB: false
},
processFacebook: false,
processMongoDb: false,
processAPI: false
}
this.handleFormSubmit = this.handleFormSubmit.bind(this);
this.apiHandler = this.apiHandler.bind(this);
this.facebookHandler = this.facebookHandler.bind(this);
this.mongodbHandler = this.mongodbHandler.bind(this);
this.updateState = this.updateState.bind(this);
};
handleFormSubmit() {
if (this.state.test.Facebook) {
fetch(AUTH.CONFIG, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(this.state.formObject)
})
.then((response) => response.json())
.then((responseData) => {
asyncStorage('config', this.state.formObject)
.then((result) =>{
Actions.login({type: ActionConst.PUSH});
alert(responseData.message);
});
});
}
};
mongodbHandler(){
this.setState({processMongoDb: false})
var errors;
if (!this.state.formObject.MongodbURL){
errors = update(this.state.errors, {
'MongodbError':{
$set: 'Enter your mongodb connection string'
}
});
}else{
errors = update(this.state.errors, {
'MongodbError':{
$set: ''
}
});
}
this.setState({errors})
if (this.state.formObject.MongodbURL){
this.setState({processMongoDb: true})
fetch('http://localhost:3000/auth/mongodb/test',{
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
MongodbURL: this.state.formObject.MongodbURL
})
})
.then((response) => response.json())
.then((responseData) =>{
let test = Object.assign({}, this.state.test, { MongoDB: true})
this.setState({test})
})
.catch((err) =>{
let test = Object.assign({}, this.state.test, { MongoDB: false})
this.setState({test})
})
}
}
facebookHandler(){
var errors;
this.setState({processFacebook: false})
if (!this.state.formObject.FacebookAppID || !this.state.formObject.FacebookDisplayName ){
errors = update(this.state.errors, {
'FacebookError':{
$set: 'Enter both facebook app id and facebook display name'
}
});
}else{
errors = update(this.state.errors, {
'FacebookError':{
$set: ''
}
})
}
this.setState({errors})
if (this.state.formObject.FacebookAppID
&& this.state.formObject.FacebookDisplayName
&& this.state.formObject.FacebookClientSecret){
this.setState({processFacebook: true})
fetch(AUTH.GRAPH,{
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
FacebookAppID: this.state.formObject.FacebookAppID,
FacebookDisplayName: this.state.formObject.FacebookDisplayName,
FacebookClientSecret: this.state.formObject.FacebookClientSecret
})
})
.then((result) => result.json())
.then((responseData) => {
let test = Object.assign({}, this.state.test, { Facebook: true});
this.setState({test});
})
.catch((error) => {
let test = Object.assign({}, this.state.test, { Facebook: false});
this.setState({test});
})
}
};
apiHandler() {
let { APIURL } = this.state.formObject;
if(APIURL) {
this.setState({processAPI: true});
fetch(APIURL, {
method: 'HEAD'
})
.then(() => {
let test = Object.assign({}, this.state.test, { API: true });
this.setState({test});
})
.catch((error) => {
let test = Object.assign({}, this.state.test, { API: false });
this.setState({test});
})
}
};
updateState(id, text) {
let newValues = update(this.state.formObject, {
[id]: {
$set: text
}
})
this.setState({formObject: newValues})
};
render(){
return(
<ScrollView style={Styles.container}>
<View style={Styles.innerView}>
<InputBox id="APIURL" placeholder="API URL" title="API URL" updateState={this.updateState}/>
{(this.state.test.API && this.state.processAPI) && <Text style={{color: 'green'}}>Test passed</Text>}
{(!this.state.test.API && this.state.processAPI) && <Text style={{color: 'red'}}>Test Failed</Text>}
<TouchableHighlight style={Styles.button} onPress={this.apiHandler}>
<Text style={Styles.buttonText}>Test API</Text>
</TouchableHighlight>
</View>
<View style={Styles.innerView}>
<InputBox id="MongodbURL" placeholder="Mongodb URL" title="Mongodb Configuration" updateState={this.updateState}/>
<Text style={{color: 'red'}}>{this.state.errors.MongodbError}</Text>
{(this.state.test.MongoDB && this.state.processMongoDb) && <Text style={{color: 'green'}}>Test passed</Text>}
{(!this.state.test.MongoDB && this.state.processMongoDb) && <Text style={{color: 'red'}}>Test Failed</Text>}
<TouchableHighlight style={Styles.button} onPress={this.mongodbHandler}>
<Text style={Styles.buttonText}>Test Mongodb</Text>
</TouchableHighlight>
</View>
<View style={Styles.innerView}>
<InputBox id="FacebookAppID" placeholder="Facebook Client ID" title="Facebook Configuration" updateState={this.updateState} />
<InputBox id="FacebookClientSecret" placeholder="Facebook Client Secret" updateState={this.updateState} />
<InputBox id="FacebookDisplayName" placeholder="Facebook Display Name" updateState={this.updateState} />
<Text style={{color: 'red'}}>{this.state.errors.FacebookError}</Text>
{(this.state.test.Facebook && this.state.processFacebook) && <Text style={{color: 'green'}}>Test passed</Text>}
{(!this.state.test.Facebook && this.state.processFacebook) && <Text style={{color: 'red'}}>Test Failed</Text>}
<TouchableHighlight style={Styles.button} onPress={this.facebookHandler}>
<Text style={Styles.buttonText}>Test facebook</Text>
</TouchableHighlight>
</View>
<View>
{this.state.test.MongoDB && this.state.test.Facebook && <TouchableHighlight style={Styles.button} onPress={this.handleFormSubmit} underlayColor='#99d9f4'>
<Text style={Styles.buttonText}>Submit</Text>
</TouchableHighlight> }
</View>
</ScrollView>
)
}
}

This error is usually encountered when you enter an infinite loop. I believe you are calling this.setState({...}); in a method that triggers a re-render.

Related

Expo-built App keeps restarting after successful built

I have created a screen where user should scan a QR code before a few button is showed to them. The function is working fine in the development environment, however when I built the APK file and try to scan a QR code, suddenly the app crash and restart.
From what I've read so far, I think the problem may be due to the async function which prevent the qrscanned handler function to run. but I may be wrong and I'm trying not to break the code. I'd like to ask if any of you can point out where should I change in order to solve the problem. The following is the code for the screen.
import React, { useState, useEffect, useContext } from 'react';
import { Text, View, StyleSheet, Button, ImageBackground, Picker, Alert, ScrollView, FlatList } from 'react-native';
import { BarCodeScanner } from 'expo-barcode-scanner';
import { TouchableOpacity } from 'react-native-gesture-handler';
import { Card } from 'react-native-paper';
import {AuthContext} from '../components/context';
const ScannerScreen = ({navigation}) => {
const { employeeNameDetails } = useContext(AuthContext);
var username = employeeNameDetails();
const [hasPermission, setHasPermission] = useState(null);
const [scanned, setScanned] = useState(false);
const [data, setData] = useState('Scan QR Code');
const [itemData, setItemData] = useState([]);
const [room, setRoom] = useState('');
const [roomName, setRoomName] = useState([]);
const [bilik, setBilik] = useState(false);
const updateRoom = () =>{
if(room != 'PILIH BILIK'){
var updateAPI = "https://example.com/api/setroom.php"
var header ={
'Accept': 'application/json',
'Content-type': 'application/json'
};
var itemdata ={
roomName: room,
asetID: data
};
fetch(
updateAPI,
{
method: 'POST',
headers: header,
body: JSON.stringify(itemdata)
}
)
.then((response)=>response.json())
.then((response)=>
{
if (response[0].isCorrect ==1){
Alert.alert(
"Berjaya","Bilik telah ditetapkan"
)}
else{
Alert.alert(
"Error","Tiada maklumat item dalam database"
)
}
}
)
.catch((error)=>{
alert("Error" + error);
})
}else{
Alert.alert(
"Error!","Sila pilih bilik."
)
}}
useEffect(() => {
(async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
const handleBarCodeScanned = ({ data }) => {
setScanned(true);
setData(data);
var searchAPI = "https://example.com/api/searchitembyid.php";
var header = {
'Accept': 'application/json',
'Content-Type': 'application/json'
};
var itemdata = {
itemid: data
};
fetch(
searchAPI,
{
method: 'POST',
headers: header,
body: JSON.stringify(itemdata)
}
)
.then((response)=>response.json())
.then((responseJSON)=>{
setItemData(responseJSON)
});
var roomAPI = "https://example.com/api/getroom.php";
var roomheader = {
'Accept': 'application/json',
'Content-Type': 'application/json'
};
var roomdata = {
username: username
};
fetch(
roomAPI,
{
method: 'POST',
headers: roomheader,
body: JSON.stringify(roomdata)
}
)
.then((response)=>response.json())
.then((responseJSON)=>{
setRoomName(responseJSON)
});
};
if (hasPermission === null) {
return <Text>Requesting for camera permission</Text>;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
const _renderItem = ({item, index}) =>{
return(
<View>
<Text style={styles.trackingNo}>{item.itemname}</Text>
<Text style={styles.trackingNo}>{item.nosiri}</Text>
</View>
)
}
return (
<ImageBackground
source={require('../assets/background-image.jpg')}
style={styles.image}>
<ScrollView style={styles.container}>
<View style={styles.top}>
<Text style={styles.title}>Scan Item</Text>
</View>
<View style={styles.barcodeSection}>
<Card>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={styles.barcode}
/></Card>
</View>
<View style={styles.bottomSection}>
<Card>
{scanned &&
<Button color="#fbf32f" title={'Scan Again'} onPress={() => {setScanned(false); setData('Scan QR Code')}} />}
{scanned &&
<View>
<FlatList
data={itemData}
renderItem={_renderItem}
keyExtractor={(item,index)=> index.toString()}
/>
<Text style={styles.btnText}>Set Room</Text>
<Picker
selectedValue={room}
style={styles.pickerInput}
onValueChange={(itemValue, itemIndex) => setRoom(itemValue)}>
{roomName.map((item, index) => {
return (< Picker.Item label={item.roomname} value={item.roomname} key={index} />);
})}
</Picker></View>
}
{scanned && <TouchableOpacity
style={styles.btn}
onPress={updateRoom}
><Text style={styles.btnText}>Tetapkan Bilik</Text></TouchableOpacity>
}
</Card></View>
</ScrollView></ImageBackground>
);
}
export default ScannerScreen;
Any suggestion or solution is greatly appreciated. Thanks guys.

Possible Unhandled Promise Rejection (id:0) : ReferenceError: Can't find variable TextInputEmail

import React,{Component} from 'react';
import {View,Text,Image,ImageBackground,
TextInput,TouchableOpacity,AsyncStorage,
ActivityIndicator,StatusBar,StyleSheet} from 'react-native';
import {createStackNavigator} from 'react-navigation-stack' ;
import {createAppContainer,createSwitchNavigator} from 'react-navigation';
import AppContainer from '../App'
import Forgot from '../Screens/Forgot' ;
class Home extends Component {
constructor(props) {
super(props);
this.state = {
TextInputEmail: '',
TextInputPassword: '',
};
}
CheckTextInput = async() =>
{
if (this.state.TextInputEmail != '')
{
if(this.state.TextInputPassword != '')
{
// if(userInfo.TextInputEmail === this.state.TextInputEmail &&
// userInfo.TextInputPassword===this.state.TextInputPassword)
fetch('http://104.197.28.169:3000/auth/login?', {
method: 'POST',
body: JSON.stringify({
"email": TextInputEmail,
"password": TextInputPassword
}),
})
.then((response) => response.json())
.then(() => {
this.props.navigation.navigate('drawernavi')
console.log('response object:',responseJson)
})
.catch((error) => {
console.error(error);
throw error;
});
// await AsyncStorage.setItem('isLoggedIn','1');
// this.props.navigation.navigate('drawernavi')
//this.userLogin()
// alert('Legged in')
// this.userLogin();
}
else
alert('Please Enter Password');
} else
alert('Please Enter Email & Password');
}
render(){
return(
<View>
<ImageBackground style={{width:'100%',height:'100%'}}
source={require('../images/login-screen.png')}>
<View style={{flex:.8,alignItems:'center',justifyContent:'center',marginTop:20}}>
<View style={styles.imageView}>
<Image style={styles.logoImg}
source = {require('../images/core-logo.png')}
></Image>
</View>
<TextInput style={{fontSize:15,borderWidth:1,borderRadius:20,paddingLeft:15,width:300,
borderColor:'black',height:40,marginTop:40}}
keyboardType='email-address'
maxLength={32}
placeholder='Email' placeholderTextColor='#a8a8a8'
onChangeText={TextInputEmail => this.setState({ TextInputEmail })}
value= {this.state.TextInputEmail}
underlineColorAndroid="transparent">
</TextInput>
<TextInput style={{width:300,fontSize:15,borderWidth:1,borderRadius:20,paddingLeft:15,
borderColor:'black',marginTop:20,height:40}}
secureTextEntry={true}
maxLength={14}
placeholder='Password' placeholderTextColor='#a8a8a8'
onChangeText={TextInputPassword => this.setState({ TextInputPassword })}
value= {this.state.TextInputPassword}
underlineColorAndroid="transparent">
</TextInput>
<TouchableOpacity
style={{width:300,marginTop:35,paddingTop:10,paddingBottom:10,backgroundColor:'#2F6995',
borderRadius:20,borderWidth: 1,borderColor: '#fff'}}
onPress={this.CheckTextInput}
underlayColor='#fff'>
<Text style={{color:'#fff',textAlign:'center', paddingLeft : 10, paddingRight : 10,fontSize:17}}>LOGIN</Text>
</TouchableOpacity>
<View style={{alignItems:'center',marginTop:30}}>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('forgotstack')} >
<Text style={{fontSize:12.5,color:'black',borderBottomWidth:1}}> Forgot Password ? </Text>
</TouchableOpacity>
</View>
</View>
</ImageBackground>
</View>
);
}
}
i am creating the functionality of login. i got this error while working on it.i am new to this. when i tried to login, the yellow warnings start appearing
i am fetching the data through a node.js api . so it is required that the input field name in api and react is same or it can be different or there is another way, please suggest correct my code if it is wrong
body: JSON.stringify({
"email": TextInputEmail,
"password": TextInputPassword
}),
//Change it to
body: JSON.stringify({
"email": this.state.TextInputEmail,
"password": this.state.TextInputPassword
}),
The problem is if(userInfo.TextInputEmail === this.state.TextInputEmail) in this conditions userInfo is not defined so userInfo.TextInputEmail is not defined .
please check once

Why react-native doesn't render all content when 2 render methods included in it?

I have some content to be rendered conditionally and some fixed content i.e. footer. I dont want to render footer every time when state changes, hence I've added two methods renderContent() and renderFooter to be called in render() method.
Below code, doesn't render both methods.
'use strict';
import React, { Component } from 'react';
import { Alert, FlatList, View, StyleSheet, Text, Linking, Button } from 'react-native';
import { AsyncStorage } from 'react-native';
import getEnvVars from '../environment';
const { apiUrl } = getEnvVars();
import Moment from 'moment';
import { Ionicons } from '#expo/vector-icons';
import FootBar from '../screens/FootBar';
import { LinesLoader } from 'react-native-indicator';
export default class SubscriptionsToEnd extends Component {
static navigationOptions = ({ navigation }) => {
const { state } = navigation;
return {
title: `${state.params && state.params.title ? state.params.title : 'Subscriptions Due'}`,
};
};
constructor(props) {
super(props);
this.state = {
isLoaded: false,
dataSource: [],
title: 'Subscriptions Due'
};
}
componentDidMount() {
this._getAllCustomers();
}
_getAllCustomers() {
let url;
if (this.state.title === 'Subscriptions Due') {
url = apiUrl + "/customersWithSubscriptionNearToEnd/";
this.props.navigation.setParams({ title: 'Subscriptions Due' })
}
if (this.state.title === 'Customers') {
url = apiUrl + "/customers/";
this.props.navigation.setParams({ title: 'Customers' })
}
this.setState({ isLoaded: false })
try {
AsyncStorage.multiGet(['role', 'jwt']).then((data) => {
let role = data[0][1];
let jwt = data[1][1];
if (role === 'Admin') {
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'jwt': jwt
},
}).then(res => res.json())
.then(
(result) => {
if (result.message != 'Unauthorized user!' && this.state.title === 'Customers') {
this.setState({
isLoaded: true,
dataSource: result,
title: 'Subscriptions Due'
});
} else if (result.message != 'Unauthorized user!' && this.state.title === 'Subscriptions Due') {
this.setState({
isLoaded: true,
dataSource: result,
title: 'Customers'
});
} else if (result.message === 'Unauthorized user!') {
this.props.navigation.navigate('Login');
}
},
(error) => {
console.log(error);
this.setState({
isLoaded: true
});
this.props.navigation.navigate('Login');
}
)
}
})
} catch (error) {
console.log('Error at getting token \n' + error)
}
}
GetGridViewItem(id) {
Alert.alert(id);
}
_logOutAsync = async () => {
await AsyncStorage.clear();
this.props.navigation.navigate('Auth');
};
_addCustomer() {
// TBD
}
renderContent() {
if (!this.state.isLoaded) {
return (
<View style={styles.loader}>
<LinesLoader color='#1d91a5' barWidth={5} barHeight={60} barNumber={5} betweenSpace={5} />
</View>
)
}
if (this.state.isLoaded) {
return (
<View style={styles.container}>
<View style={styles.grid}>
<FlatList
data={this.state.dataSource}
renderItem={({ item }) =>
<View style={styles.GridViewContainer}>
<Text style={styles.GridViewTextLayout}>
<Text onPress={this.GetGridViewItem.bind(this, item._id)}>
<Text style={styles.Name}>{item.firstname}</Text> <Text style={styles.Name}>{item.lastname}</Text> {"\n"}
<Text>{Moment(item.till_date).format('Do MMM YYYY')} </Text>{"\n\n"}
</Text>
<Text onPress={() => { Linking.openURL('tel:+44' + item.mobile); }}><Ionicons name="md-phone-portrait" size={22} color="#1d91a5" /> {item.mobile}</Text> {"\n\n"}
<Text><Ionicons name="md-mail" size={22} color="#1d91a5" />{item.email}</Text>
</Text>
</View>}
numColumns={2}
keyExtractor={(item, index) => index.toString()}
/>
</View >
</View>
)
};
}
renderFooter() {
return (
<View style={styles.buttonsContainer}>
<View style={styles.button}>
<Button color='#1d91a5' title={this.state.title} onPress={this._getAllCustomers.bind(this)} />
</View>
<View style={styles.button}>
<Button color='#1d91a5' title="+Customer" onPress={this._addCustomer.bind(this)} />
</View>
<View style={styles.button}>
<Button color='#1d91a5' title="Logout" onPress={this._logOutAsync.bind(this)} />
</View>
</View>
);
}
render() {
return (
this.renderContent(),
this.renderFooter()
);
}
}
Above code only renders this.renderFooter() method. If I swap methods in render(), it renders this.renderContent().
Can someone please tell me why it is failing to render both?
I was doing it wrong. Main render() method should be like:
render() {
return (
<View style={styles.wrapper}>
{this.renderContent()}
{this.renderFooter()}
</View>
);
}
It looks like you figured it out just before I could post my answer.
The return function can only return one view. Your 2 functions each return a view. So wrapping both functions in a single view solves the problem.

Passing data from one component to another in React Native

I am setting Sub Domain URL's for single app. Sub domain name will enter at the first time. it saves to the async storage and need to retrieve it from a common component
Using the const, it's not working properly.
Here is the partially completed code. baseURL and socketURL is needed inside another component function. How can I access these constants from there ?
index_new.js
import * as React from 'react';
import { View } from 'react-native';
import AsyncStorage from '#react-native-community/async-storage';
import Login from "../screens/common/login/login/login";
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
sub_domain: '',
};
}
async getSchoolCode() {
let sub_domain = '';
try {
sub_domain = await AsyncStorage.getItem('SCHOOL_CODE') || ''
} catch (error) {
}
return sub_domain;
};
async setSubdomain() {
const sub_domain = await this.getschoolcode()
await this.setState({ sub_domain })
}
getBaseUrl() {
return `http://${this.state.sub_domain}.vidhyadhan.in:81/`;
}
getSocketIoUrl() {
return `http://${this.state.sub_domain}.vidhyadhan.in:8080/`;
}
async componentDidMount() {
await this.setSubdomain();
}
render() {
const baseUrl = this.getBaseUrl();
const socketIoUrl = this.getSocketIoUrl();
const extraProps = {
baseUrl,
socketIoUrl
}
return (
<View>
<Login {...extraProps} />
</View>
)
}
}
Login.js
import React, { Component } from 'react'
import {
Alert,
Keyboard,
Text,
View,
TextInput,
TouchableHighlight,
Image,
ActivityIndicator,
StatusBar,
} from 'react-native'
import config from "../../../../config";
import styles from './style'
import { Icon } from "react-native-elements";
import Toaster from '../../../../components/toaster'
import AsyncStorage from '#react-native-community/async-storage';
class Login extends Component {
constructor(props) {
super(props);
this.state = {
credentials: {
schoolcode: "",
email: "",
password: "",
},
loading: false,
school_code: '',
};
}
async getschoolcode() {
let school_code = '';
try {
school_code = await AsyncStorage.getItem('SCHOOL_CODE') || ''
} catch (error) {
}
return school_code;
};
updateText(text, field) {
let newCredentials = Object.assign(this.state.credentials);
newCredentials[field] = text;
// setState should be done like this
this.setState({
credentials: newCredentials
})
if(field == 'schoolcode'){
AsyncStorage.setItem('SCHOOL_CODE', text);
this.getschoolcode().then((keyValue) => {
this.state.school_code = keyValue;
console.log(this.state.school_code);
});
}
}
async login() {
Keyboard.dismiss();
let credentials = this.state.credentials;
if (this.state.credentials.schoolcode == '' || this.state.credentials.email == '' || this.state.credentials.password == '') {
Toaster.toast('Please Enter a valid UserName and Password', '#d30000')
} else {
const that = this;
credentials.email = that.state.credentials.email;
this.setState({ loading: !this.state.loading });
const new_url = this.props.baseUrl;
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 => {
if (responseJson.confirmation === "success") {
AsyncStorage.setItem('USER_ID', responseJson.data.user_id.toString());
this.setState({ loading: !this.state.loading });
setTimeout(() => {
this.props.navigation.navigate("Home")
}, 500);
} else {
this.setState({ loading: !this.state.loading });
setTimeout(() => {
Toaster.toast('Please Enter a valid UserName and Password', '#d30000')
// 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')) {
Toaster.toast('Please check your internet connection or try again later', '#d30000')
}
}, 500);
})
}
}
render() {
const loginText = (this.state.loading) ? 'Loading' : 'Login';
return (
<View style={styles.container}>
<StatusBar backgroundColor="#2383c9"
translucent={true}
hidden={false}/>
<Image source={require('../../../../assets/images/icons/logo.png')}
style={{ width: 99, height: 99, margin: 5, }} />
<Text style={{ fontSize: 20, margin: 20, color: "#ffffff" }}>Vidhyadhan</Text>
<View style={styles.inputContainer}>
<Image style={styles.inputIcon}
source={require('../../../../assets/images/icons/username.png')} />
<TextInput style={styles.inputs}
placeholder="School-Code"
underlineColorAndroid='transparent'
onChangeText={text => {
this.updateText(text, 'schoolcode')
}} value={this.state.schoolcode}
autoCorrect={false}
autoCapitalize={"none"}
/>
</View>
<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;
First, You're not setting the state correctly. Then, you're trying to set the state twice.
Here's a better way of doing it:
import * as React from 'react';
import { View } from 'react-native';
import AsyncStorage from '#react-native-community/async-storage';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
sub_domain: '',
};
}
async getSchoolCode() {
let sub_domain = '';
try {
sub_domain = await AsyncStorage.getItem('sub_domain') || ''
} catch (error) {
}
return sub_domain;
};
async setSubdomain() {
const sub_domain = await this.getschoolcode()
await this.setState({ sub_domain })
}
getBaseUrl() {
return `http://${this.state.sub_domain}.vidhyadhan.in:81/`;
}
getSocketIoUrl() {
return `http://${this.state.sub_domain}.vidhyadhan.in:8080/`;
}
async componentDidMount() {
await this.setSubdomain();
}
render() {
const baseUrl = this.getBaseUrl();
const socketIoUrl = this.getSocketIoUrl();
return (
<View/>
);
}
}
Based on the comments, here's how your render can be:
render() {
const baseUrl = this.getBaseUrl();
const socketIoUrl = this.getSocketIoUrl();
const extraProps = {
baseUrl,
socketIoUrl
}
return (
<View>
<MyFirstComponent {...extraProps} />
<MySecondComponent {...extraProps} />
</View>
)
}
And in your MyFirstComponent, you can either use this.props.baseUrl or this.props.socketIoUrl

How to upload a photo to a backend server along with form data using API?

Here below is my code, I am trying to capture an image and filling the other input field. Once I click on create button, I am calling an api to upload it to the backend server. But how Do I send the file details to backend? I have the working api in postman. And some of the fields I have not included some of the input field code here because the code will be large here.
API takes the following fields in postman
import React, {Component} from 'react';
import {
StyleSheet,
Button,
TextInput,
Text,
TouchableOpacity,
View,
Picker,
Image,
} from 'react-native';
import ImagePicker from 'react-native-image-picker';
export default class CreateStudent extends React.Component {
constructor(props){
super(props);
this.state = {
studentName: '',
email: '',
newPassword: '',
fileShow: '',
faceDetails: '',
school: 1,
}
}
handleEmail = (text) => {
this.setState({ email: text })
}
handlePassword = (text) => {
this.setState({ newPassword: text })
}
handleName = (text) => {
this.setState({ studentName: text })
}
selectImage = () => {
const options = {
quality: 1.0,
maxWidth: 75,
maxHeight: 75
}
ImagePicker.launchCamera(options,(responce)=>{
const fileDetails ={
uri : responce.uri,
name :responce.fileName,
method: 'POST',
width : 50,
height : 50,
path : responce.path,
type : responce.type,
}
this.setState({
fileShow: responce.uri,
faceDetails: fileDetails
})
console.log(this.state.fileShow);
console.log(fileDetails);
})
}
async onCreateStudentPressed() {
try {
let response = await fetch('http://10.0.2.2:5000/api/students/create', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
file: this.state.fileDetails,
email: this.state.email,
password: this.state.newPassword,
phone_number: this.state.phoneNumber,
class: this.state.class,
section: this.state.section,
school: this.state.school,
name: this.state.studentName
})
});
let res = await response.text();
if (response.status >= 200 && response.status < 300) {
//Handle success
let accessToken = res;
console.log(accessToken);
//On success we will store the access_token in the AsyncStorage
//this.storeToken(accessToken);
} else {
//Handle error
let error = res;
throw error;
}
} catch(error) {
this.setState({error: error});
console.log("error " + error);
this.setState({
passwordUpdated: false
})
}
}
render() {
return (
<View>
<View style={styles.formContainer}>
<Text style ={styles.pageTitle} >Create Student</Text>
<View style={styles.Camera}>
<TouchableOpacity onPress={this.selectImage}>
<Text>Take Picture</Text>
</TouchableOpacity>
</View>
{
this.state.fileShow !== '' ?
<View style={styles.showBlack}>
<View style={styles.placeholder}>
<Image key={1} source={{uri: this.state.fileShow}} style={styles.previewImage} />
</View>
</View>
:
null
}
<TextInput
placeholder='Enter Student Name'
placeholderTextColor='#808080'
style ={styles.textInput}
underlineColorAndroid={'transparent'}
onChangeText = {this.handleName}
/>
<TextInput
placeholder='Enter Student Email'
placeholderTextColor='#808080'
style ={styles.textInput}
underlineColorAndroid={'transparent'}
onChangeText = {this.handleEmail}
/>
<TextInput
placeholder='Enter Password'
placeholderTextColor='#808080'
secureTextEntry={true}
style ={styles.textInput}
underlineColorAndroid={'transparent'}
onChangeText = {this.handlePassword}
/>
<Button
onPress={this.onCreateStudentPressed.bind(this)}
style ={styles.loginBtn}
title="Create"
/>
</View>
</View>
);
}
}
There are various ways you can achieve this. I am going to highlight the easiest way for you to do this. I assume you have a storage service such as Amazon S3 where you store the photo. The easiest way is for you to first upload the image to your S3, the response body will include the url to the uploaded photo. Then you can take that url together with your form values and pass them to your backend.
//Example.js
import React, { Component } from 'react';
import {
//Whatever RN Components you need
StyleSheet,
Text,
View,
Alert,
TouchableHighlight,
TextInput,
ActivityIndicator,
ScrollView,
Picker,
Linking,
Image,
} from 'react-native';
import axios from 'axios';
import { RNS3 } from 'react-native-aws3';
import ImagePicker from 'react-native-image-picker';
export default class RegisterScreen extends Component {
state = {
email: '',
password: '',
confirmedPassword: '',
firstName: '',
lastName: '',
avatar: '',
avatarUri: '',
avatarName: '',
avatarType: '',
loading: false,
errorMessage: '',
uploadMessage: ''
}
onRegisterPress = () => {
this.setState({
errorMessage: '',
registerLoading: true,
uploadMessage: 'This may take a while: Uploading your Profile Pic...'
});
const avatarFile = {
uri: this.state.avatarUri,
name: this.state.avatarName,
type: this.state.avatarType
};
const avatarOptions = {
keyPrefix: 'uploads/path/avatar/' + this.state.email + '/' + new Date().getTime(),
bucket: 'your-bucket-s3',
region: 'us-west-2',
accessKey: 'ACCESSKEY',
secretKey: 'SECRETKEY',
successActionStatus: 201
};
RNS3.put(avatarFile, avatarOptions)
.then((response) => {
if (response.status === 201) {
this.setState({ avatar: response.body.postResponse.location });
} else {//handle error}
})
.then(() => {
this.setState({
uploadMessage: 'Creating your profile'
});
const url = 'someremoteurl.com';
const payload = {
email: this.state.email,
password: this.state.password,
password_confirmation: this.state.confirmedPassword,
first_name: this.state.firstName,
last_name: this.state.lastName,
avatar: this.state.avatar,
};
axios.post(url, payload)
.then((r) => {
this.setState({
uploadMessage: '',
loading: false
});
if (r.status === 200) {
//handle success
}
})
.catch(() => {
// handle form submission error
this.setState({
uploadMessage: '',
loading: false,
error: true
});
});
});
}
openMediaLibrary() {
const options = {
title: 'Select Photo',
storageOptions: {
skipBackup: true,
path: 'images'
}
};
ImagePicker.showImagePicker(options, (response) => {
console.log('Response = ', response);
if (response.didCancel) {
console.log('Cancelled selection of image');
} else if (response.error) {
console.log('Image Picker error', response.error)
} else {
this.setState({
avatarUri: response.uri,
avatarName: response.fileName,
avatarType: response.type
});
}
});
}
render() {
return (
<ScrollView style={{ margin: 15 }}>
<View style={styles.formField}>
<Text style={styles.formLabelStyle}> Email</Text>
<TextInput
placeholder='john#yahoo.com'
underlineColorAndroid='transparent'
onChangeText={(email) => this.setState({ email })}
value={this.state.email}
autoCapitalize='none'
/>
</View>
<View style={styles.formField}>
<Text style={styles.formLabelStyle}> Password (minimum of 8 letters)</Text>
<TextInput
placeholder='password'
underlineColorAndroid='transparent'
onChangeText={(password) => this.setState({ password })}
value={this.state.password}
secureTextEntry
autoCorrect={false}
autoCapitalize='none'
/>
</View>
<View style={styles.formField}>
<Text style={styles.formLabelStyle}> Password Confirmation</Text>
<TextInput
placeholder='retype your password'
underlineColorAndroid='transparent'
onChangeText={(confirmedPassword) => this.setState({ confirmedPassword })}
value={this.state.confirmedPassword}
secureTextEntry
autoCorrect={false}
autoCapitalize='none'
/>
</View>
<View style={styles.formField}>
<Text style={styles.formLabelStyle}> First Name</Text>
<TextInput
placeholder='First Name'
underlineColorAndroid='transparent'
onChangeText={(firstName) => this.setState({ firstName })}
value={this.state.firstName}
/>
</View>
<View style={styles.formField}>
<Text style={styles.formLabelStyle}>Last Name</Text>
<TextInput
placeholder='Last Name'
underlineColorAndroid='transparent'
onChangeText={(lastName) => this.setState({ lastName })}
value={this.state.lastName}
/>
</View>
<View>
<View style={{ padding: 10 }}>
{
this.state.avatarUri.length > 0 ?
<Image
source={{ uri: this.state.avatarUri }}
style={{ width: 140, height: 140 }}
/>
: null
}
</View>
<Button
small
backgroundColor='#13CE66'
title={(this.state.avatarUri.length === 0) ?
'Choose Profile Picture' : 'Change Profile Picture'}
onPress={() => this.openMediaLibrary()}
/>
</View>
<Button
large
backgroundColor='#13CE66'
title='Submit'
onPress={() => this.onRegisterPress() }
/>
</ScrollView>
)
}
}
So when you attempt to register this user, the image is first uploaded and the retrieved url is stored in state to be used as part of form params for avatar.
This is easy, the downside is having to wait for upload response before actually having to send your form params
EDIT
If you are not using a 3rd party storage service or if you would rather send the file to your own servers then use FormData():
//OnRegisterPress function should look like this:
onRegisterPress = () => {
let form = new FormData();
form.append('avatar', {
//from openMedia Function, image selected with image-picker
uri: this.state.avatarUri,
name: this.state.avatarName,
type: this.state.avatarType
});
// append your other params such as email, password, name
form.append('email', this.state.email)
form.append('password', this.state.password)
// when you are done appending, use fetch to send the form data as axios seems to be having a bit of issues doing this.
fetch('http://yourapiendpoint', {
headers: {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data'
},
method: 'POST',
body: form
});
}