Passing data from one component to another in React Native - 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

Related

How to deal with delay api response in react native using redux?

Im building a react-native app, which when the user try to signIn, I invoike firebase.CreateUser and then an api from firebase function to create that user in my database (Firebase Real-Time). The problem is that when the componentDidUpdate is executed, I still don't have the result from my firebaseFunction, then my props only update if I tap in screen. I would like to know how to deal with that.
Im using redux.
Follow my code:
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, Button, Image,Alert} from 'react-native';
import logo from '../../asserts/logo.png'
import { TouchableOpacity, TextInput } from 'react-native-gesture-handler';
import { Divider,Input} from 'react-native-elements';
import axios from 'axios';
import { connect } from 'react-redux';
import { signupUser} from '../../store/actions/Section/actions';
class Signin extends Component {
state = {
fullName:'',
userName:'',
email:'',
password:'',
confirmPassword:'',
bornDate:'',
State:'',
City:''
};
handleEmailChange = val => {
this.setState({ email:val });
};
handlePasswordChange = val => {
this.setState({ password:val });
};
handleConfirmPasswordChange = val => {
this.setState({ confirmPassword:val });
};
handleNameChange = val => {
this.setState({ fullName:val });
};
handleUserNameChange = val => {
this.setState({ userName:val });
};
handleStateChange = val => {
this.setState({ State:val });
};
handleCityChange = val => {
this.setState({ City:val });
};
handleBornDateChange = val => {
this.setState({ bornDate:val });
};
onSignInUser = () => {
const {email,password} = this.state
if(email=='' || password=='')
return;
this.props.signUp(this.state.fullName,this.state.userName, this.state.email,this.state.password,this.state.confirmPassword,this.state.bornDate,this.state.State,this.state.City);
// this.props.navigation.navigate('User');
};
componentDidUpdate() {
const { idUser, loading,error } = this.props;
console.log(idUser);
console.log('aqui');
if (!loading && error) Alert.alert('Erro', error);
if (!loading && idUser) this.props.navigation.navigate('User');
}
render() {
return (
<View style={styles.container}>
<View style={styles.flexCenter}>
<Image source={logo} style={styles.logoImage}/>
<Text style={styles.logoText} >HomeShare</Text>
<Text style={styles.sublogoText} >SignUp</Text>
</View>
<Divider style={styles.divider} />
<View style={styles.flexButton}>
<View style={styles.inputContainer}>
<Input style={styles.textInput} onChangeText={this.handleNameChange} value={this.state.fullName} placeholder='Nome'/>
<Input style={styles.textInput} onChangeText={this.handleUserNameChange} value={this.state.userName} placeholder='User'/>
<Input style={styles.textInput} onChangeText={this.handleBornDateChange} value={this.state.bornDate} placeholder='Nascimento'/>
<Input style={styles.textInput} onChangeText={this.handleStateChange} value={this.state.State} placeholder='Estado'/>
<Input style={styles.textInput } onChangeText={this.handleCityChange} value={this.state.City} placeholder='Cidade'/>
<Input style={styles.textInput} onChangeText={this.handleEmailChange} value={this.state.email} placeholder='E-mail' keyboardType={'email-address'}/>
<Input style={styles.textInput} onChangeText={this.handlePasswordChange} value={this.state.password} placeholder='Senha' secureTextEntry={true}/>
<Input style={styles.textInput} onChangeText={this.handleConfirmPasswordChange} value={this.state.confirmPassword} placeholder='Confirme sua Senha' secureTextEntry={true}/>
</View>
<TouchableOpacity style={styles.button} activeOpacity={0.5} onPress={this.onSignInUser} >
<View>
<Text style={styles.buttonText}>SignIn</Text>
</View>
</TouchableOpacity>
<Text style={{marginTop:10}}>Ou</Text>
<TouchableOpacity style={styles.button} activeOpacity={0.5} onPress={this.signInUser}>
<View>
<Text style={styles.buttonText}>Entrar com Facebook</Text>
</View>
</TouchableOpacity>
</View>
</View>
);
}
}
const mapStateToProps = ({ section: { restoring, loading, user, error, logged, idUser } }) => ({
restoring: restoring,
loading: loading,
user: user,
error: error,
logged: logged,
idUser: idUser
});
const mapDispatchToProps = {
signUp:signupUser
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(Signin);
My Action:
export const signupUser = (fullName,userName, email,password,confirmPassword,bornDate,State,City) => dispatch => { dispatch(sessionLoading());
firebaseService.auth().createUserWithEmailAndPassword(email, password).then(user => {
console.log(user);
firebaseService.auth().currentUser.getIdToken(true).then(function(idToken) {
SectionService.signIn(idToken,fullName,userName, email,password,confirmPassword,bornDate,State,City).then((response) =>{
console.log(response);
dispatch(sessionSetId(response));
}).catch(e=> {
dispatch(sessionError(e));
});
}).catch(function(error) {
dispatch(sessionError(e));
});
})
.catch(error => {
dispatch(sessionError(error.message));
});
A proposed solution is to handle the account creation in the createUser callback and to update it with other data in the cloud function. Alternatively you can set up a listener that looks for the document, which will then be created and the listener will be notified.
I personally create the user doc on the client side because I create it with some data only available on the client, but your use case will be dictate your preferred approach.

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.

How to expand and collapse specify section using SecionList?

I call an api https://obscure-reaches-65656.herokuapp.com/api/getCloseTime?city=Miaoli&sTime=21&eTime=24 to my react-redux action and use SectionList to show the data.
With official tutorial, i use SectionList it will just show all of the section, i try to find the way when click title that can expand or collapse the section.
I find my sectionComp and renderSectionItem use the same title so i try use this.state{ title: '', expand: false }
When i click 國興戲院 use this.setState({ title: '國興戲院', expand: true }) and use like if(this.state.expand) {} in renderSectionItem
Obviously its not working because i may have a lot of section.
I have no idea what next step should i try.
Any help would be appreciated. Thanks in advance.
Here is my SectionList data:
Here is my class component:
import React, { Component } from 'react';
import { Text, SectionList, TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';
import { View } from 'react-native-animatable';
import { fetchSearchTime } from '../actions';
import { Spinner } from './common';
import GetUserTime from '../function/common/GetUserTime';
class MovieCloseTime extends Component {
constructor(props) {
super(props);
const { selectedCity, firstSliderValue, secondSliderValue }
= this.props.navigation.state.params;
this.state = {
selectedCity,
firstSliderValue,
secondSliderValue,
};
}
componentDidMount() {
const { selectedCity, firstSliderValue, secondSliderValue } = this.state;
this.props.fetchSearchTime({
selectedCity, firstSliderValue, secondSliderValue
});
}
sectionComp = (info) => {
const theaterCn = info.section.title;
console.log('Title info is =>');
console.log(info);
return (
<TouchableOpacity
onPress={() => console.log('Hot to expand and collapse specify section when click here?')}
>
<View style={{ flex: 1, backgroundColor: '#DCDCDC' }}>
<Text style={styles.sectionTitle}>{theaterCn}</Text>
</View>
</TouchableOpacity>
);
}
renderSectionItem = (info) => {
const cnName = info.item.cnName;
console.log('Section info is =>');
console.log(info);
return (
<TouchableOpacity
onPress={() => {
this.props.navigation.navigate('MovieDetail', {
enCity: this.state.selectedCity,
cnName
});
}
}
>
<View style={{ flex: 1, flexDirection: 'column' }}>
<Text style={styles.sectionSubTitle}>{cnName}</Text>
<View style={{ flexDirection: 'row', flexWrap: 'wrap', backgroundColor: '#F8F8FF' }}>
{info.item.releasedTime.map((value, index) => {
const theTime = GetUserTime.getAsiaTime(value, 'YYYY/MM/DD HH:mm:ss');
const hour = theTime.getHours();
const minute = (theTime.getMinutes() < 10 ? '0' : '') + theTime.getMinutes();
return (
<Text style={styles.sectionTimeTitle} key={index}>{`${hour}:${minute}`}</Text>
);
})
}
</View>
</View>
</TouchableOpacity>
);
}
render() {
const movieData = this.props.searchTime;
if (this.props.loading) {
return <Spinner text='Loading...' />;
}
console.log('movieData is =>');
console.log(movieData);
return (
<View style={{ flex: 1 }}>
<SectionList
renderSectionHeader={this.sectionComp}
renderItem={this.renderSectionItem}
sections={movieData}
keyExtractor={(item, index) => index}
ItemSeparatorComponent={() => <View style={styles.separator} />}
/>
</View>
);
}
}
const mapStateToProps = (state) => {
const searchTime = state.searchTime.searchList;
const loading = state.searchTime.loading;
return { searchTime, loading };
};
const styles = {
// some styles
};
export default connect(mapStateToProps, { fetchSearchTime })(MovieCloseTime);
Here is my action fetchSearchTime:
export const fetchSearchTime = ({ selectedCity, firstSliderValue, secondSliderValue }) => {
return (dispatch) => {
dispatch({ type: SEARCH_TIME_REQUEST });
console.log(`https://obscure-reaches-65656.herokuapp.com/api/getCloseTime?city=${selectedCity}&sTime=${firstSliderValue}&eTime=${secondSliderValue}`);
fetch(`https://obscure-reaches-65656.herokuapp.com/api/getCloseTime?city=${selectedCity}&sTime=${firstSliderValue}&eTime=${secondSliderValue}`)
.then(response => response.json())
.then(responseData => {
const movieData = responseData.reduce((r, s) => {
r.push({ title: s.theaterCn, id: s._id, expand: true, data: s.movie });
return r;
}, []);
//dispatch({ type: SEARCH_TIME, payload: responseData });
dispatch({ type: SEARCH_TIME, payload: movieData });
})
.catch((error) => console.log(error));
};
};
about type SEARCH_TIME reducer:
// with others type
import {
SEARCH_TIME_REQUEST,
SEARCH_TIME
} from '../actions/types';
const INITIAL_STATE = {
searchList: [],
loading: true,
selectedCity: '',
firstSliderValue: '',
secondSliderValue: ''
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case SEARCH_TIME_REQUEST:
return {
searchList: [],
loading: true,
};
case SEARCH_TIME:
return {
searchList: action.payload,
loading: false
};
default:
return state;
}
};

Fetch -React Native.Error:on function

undefined is not a function (evaluating 'this_submitForm()')....*I am getting this error while calling the function.I am new to react native and I have searched Every where But i didn't get any solution.If solution available please add sample code for better understanding.
I am posting my code here:*
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
View,
Navigator,
TouchableOpacity,
} = React;
var SCREEN_WIDTH = require('Dimensions').get('window').width;
var PageOne = React.createClass({
onEmailChange(email) {
let s = this.state;
s.email = email;
this.setState(s);
},
render() {
return (
<View style={[styles.container, {backgroundColor: 'green'}]}>
<Text style={styles.welcome}>Greetings!</Text>
<TouchableOpacity onPress={this._handlePress}>
<View style={{paddingVertical: 10, paddingHorizontal: 20, backgroundColor: 'black'}}>
<TextInput
onChangeText={this.onEmailChange}
placeholderTextColor="#a0a0a0"
placeholder="email"
underlineColorAndroid='transparent'/>
</View>
</TouchableOpacity onPress={this._submitForm()}>
</View>
)
},
});
var PageTwo = React.createClass({
render() {
return (
<View style={[styles.container, {backgroundColor: 'green'}]}>
<Text style={styles.welcome}>Greetings!</Text>
<TouchableOpacity onPress={this._handlePress}>
<View style={{paddingVertical: 10, paddingHorizontal: 20, backgroundColor: 'black'}}>
<Text style={styles.welcome}>Go to page two</Text>
</View>
</TouchableOpacity>
</View>
)
},
});
class VerifyMe extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
}
}
_renderScene(route, navigator) {
if (route.id === 1) {
return <PageOne navigator={navigator}/>
} else if (route.id === 2) {
return <PageTwo navigator={navigator}/>
}
}
_submitForm() {
fetch(baseURL+'users/loginUser', {
method: 'post',
body: JSON.stringify({
config_name: 'default',
email: this.state.email,
})
.then((response) => response.json())
.then((responseJson) => {
if (response.data.responsecode === 1) {
this.props.navigator.push({id: 2,});
}
})
.catch((error) => {
console.error(error);
})
});
}
render() {
return (
);
}
}
You are trying to use this._submitFrom in PageOne but you defined it in PageTwo, try to use following code
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
View,
Navigator,
TouchableOpacity,
} = React;
var SCREEN_WIDTH = require('Dimensions').get('window').width;
var PageOne = React.createClass({
getInitialState () {
return {
email:'', //add your initial state for this class here
};
},
onEmailChange(email) {
//let s = this.state;
//s.email = email; // no need of these lines
this.setState({
email:email
});
},
_submitForm() {
fetch(baseURL+'users/loginUser', {
method: 'post', body: JSON.stringify({ config_name: 'default', email: this.state.email
})
.then((response) => response.json())
.then((responseJson) => {
if (response.data.responsecode === 1) {
this.props.navigator.push({id: 2,});
}
})
.catch((error) => {
console.error(error);
})
});
}
render() {
return (
<View style={[styles.container, {backgroundColor: 'green'}]}>
<Text style={styles.welcome}>Greetings!</Text>
<TouchableOpacity onPress={this._handlePress}>
<View style={{paddingVertical: 10, paddingHorizontal: 20, backgroundColor: 'black'}}>
<TextInput
onChangeText={this.onEmailChange}
placeholderTextColor="#a0a0a0"
placeholder="email"
underlineColorAndroid='transparent'/>
</View>
</TouchableOpacity onPress={this._submitForm.bind(this)}> //need to use bind
</View>
)
},
});
var PageTwo = React.createClass({
render() {
return (
<View style={[styles.container, {backgroundColor: 'green'}]}>
<Text style={styles.welcome}>Greetings!</Text>
<TouchableOpacity onPress={this._handlePress}>
<View style={{paddingVertical: 10, paddingHorizontal: 20, backgroundColor: 'black'}}>
<Text style={styles.welcome}>Go to page two</Text>
</View>
</TouchableOpacity>
</View>
)
},
});
class VerifyMe extends Component {
constructor(props) {
super(props);
this.state = {
email: '',
password: '',
}
}
_renderScene(route, navigator) {
if (route.id === 1) {
return <PageOne navigator={navigator}/>
} else if (route.id === 2) {
return <PageTwo navigator={navigator}/>
}
}
} render() { return ( ); } }

Using react native to Share a link and String not working

In using react native share, I come across this error. My code can be seen below, I am not entirely certain what it means.
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. Check the render method of Tabs.
Share.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
TouchableOpacity,
View,
Clipboard,
ToastAndroid,
AlertIOS,
Platform
} from 'react-native';
import Share, {ShareSheet, Button} from 'react-native-share';
class TestShare extends Component {
constructor(props) {
super(props);
this.state = {
visible: false
}
}
onCancel() {
console.log("CANCEL")
this.setState({visible:false});
}
onOpen() {
console.log("OPEN")
this.setState({visible:true});
}
render() {
let shareOptions = {
title: "React Native",
message: "Hola mundo",
url: "http://facebook.github.io/react-native/",
subject: "Share Link" // for email
};
let shareImageBase64 = {
title: "React Native",
message: "Hola mundo",
url: REACT_ICON,
subject: "Share Link" // for email
};
return (
<View style={styles.container}>
<TouchableOpacity onPress={()=>{
Share.open(shareImageBase64);
}}>
<View style={styles.instructions}>
<Text>Simple Share Image Base 64</Text>
</View>
</TouchableOpacity>
<TouchableOpacity onPress={()=>{
Share.open(shareOptions);
}}>
<View style={styles.instructions}>
<Text>Simple Share</Text>
</View>
</TouchableOpacity>
<TouchableOpacity onPress={this.onOpen.bind(this)}>
<View style={styles.instructions}>
<Text>Share UI Component</Text>
</View>
</TouchableOpacity>
<ShareSheet visible={this.state.visible} onCancel={this.onCancel.bind(this)}>
<Button iconSrc={{ uri: TWITTER_ICON }}
onPress={()=>{
this.onCancel();
setTimeout(() => {
Share.shareSingle(Object.assign(shareOptions, {
"social": "twitter"
}));
},300);
}}>Twitter</Button>
<Button iconSrc={{ uri: FACEBOOK_ICON }}
onPress={()=>{
this.onCancel();
setTimeout(() => {
Share.shareSingle(Object.assign(shareOptions, {
"social": "facebook"
}));
},300);
}}>Facebook</Button>
<Button iconSrc={{ uri: WHATSAPP_ICON }}
onPress={()=>{
this.onCancel();
setTimeout(() => {
Share.shareSingle(Object.assign(shareOptions, {
"social": "whatsapp"
}));
},300);
}}>Whatsapp</Button>
<Button iconSrc={{ uri: GOOGLE_PLUS_ICON }}
onPress={()=>{
this.onCancel();
setTimeout(() => {
Share.shareSingle(Object.assign(shareOptions, {
"social": "googleplus"
}));
},300);
}}>Google +</Button>
<Button iconSrc={{ uri: EMAIL_ICON }}
onPress={()=>{
this.onCancel();
setTimeout(() => {
Share.shareSingle(Object.assign(shareOptions, {
"social": "email"
}));
},300);
}}>Email</Button>
<Button
iconSrc={{ uri: CLIPBOARD_ICON }}
onPress={()=>{
this.onCancel();
setTimeout(() => {
if(typeof shareOptions["url"] !== undefined) {
Clipboard.setString(shareOptions["url"]);
if (Platform.OS === "android") {
ToastAndroid.show('Link copiado al portapapeles', ToastAndroid.SHORT);
} else if (Platform.OS === "ios") {
AlertIOS.alert('Link copiado al portapapeles');
}
}
},300);
}}>Copy Link</Button>
<Button iconSrc={{ uri: MORE_ICON }}
onPress={()=>{
this.onCancel();
setTimeout(() => {
Share.open(shareOptions)
},300);
}}>More</Button>
</ShareSheet>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
instructions: {
marginTop: 20,
marginBottom: 20,
},
});
// twitter icon
const TWITTER_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAABvFBMVEUAAAAA//8AnuwAnOsAneoAm+oAm+oAm+oAm+oAm+kAnuwAmf8An+0AqtUAku0AnesAm+oAm+oAnesAqv8An+oAnuoAneoAnOkAmOoAm+oAm+oAn98AnOoAm+oAm+oAmuoAm+oAmekAnOsAm+sAmeYAnusAm+oAnOoAme0AnOoAnesAp+0Av/8Am+oAm+sAmuoAn+oAm+oAnOoAgP8Am+sAm+oAmuoAm+oAmusAmucAnOwAm+oAmusAm+oAm+oAm+kAmusAougAnOsAmukAn+wAm+sAnesAmeoAnekAmewAm+oAnOkAl+cAm+oAm+oAmukAn+sAmukAn+0Am+oAmOoAmesAm+oAm+oAm+kAme4AmesAm+oAjuMAmusAmuwAm+kAm+oAmuoAsesAm+0Am+oAneoAm+wAmusAm+oAm+oAm+gAnewAm+oAle0Am+oAm+oAmeYAmeoAmukAoOcAmuoAm+oAm+wAmuoAneoAnOkAgP8Am+oAm+oAn+8An+wAmusAnuwAs+YAmegAm+oAm+oAm+oAmuwAm+oAm+kAnesAmuoAmukAm+sAnukAnusAm+oAmuoAnOsAmukAqv9m+G5fAAAAlHRSTlMAAUSj3/v625IuNwVVBg6Z//J1Axhft5ol9ZEIrP7P8eIjZJcKdOU+RoO0HQTjtblK3VUCM/dg/a8rXesm9vSkTAtnaJ/gom5GKGNdINz4U1hRRdc+gPDm+R5L0wnQnUXzVg04uoVSW6HuIZGFHd7WFDxHK7P8eIbFsQRhrhBQtJAKN0prnKLvjBowjn8igenQfkQGdD8A7wAAAXRJREFUSMdjYBgFo2AUDCXAyMTMwsrGzsEJ5nBx41HKw4smwMfPKgAGgkLCIqJi4nj0SkhKoRotLSMAA7Jy8gIKing0KwkIKKsgC6gKIAM1dREN3Jo1gSq0tBF8HV1kvax6+moG+DULGBoZw/gmAqjA1Ay/s4HA3MISyrdC1WtthC9ebGwhquzsHRxBfCdUzc74Y9UFrtDVzd3D0wtVszd+zT6+KKr9UDX749UbEBgULIAbhODVHCoQFo5bb0QkXs1RAvhAtDFezTGx+DTHEchD8Ql4NCcSyoGJYTj1siQRzL/JKeY4NKcSzvxp6RmSWPVmZhHWnI3L1TlEFDu5edj15hcQU2gVqmHTa1pEXJFXXFKKqbmM2ALTuLC8Ak1vZRXRxa1xtS6q3ppaYrXG1NWjai1taCRCG6dJU3NLqy+ak10DGImx07LNFCOk2js6iXVyVzcLai7s6SWlbnIs6rOIbi8ViOifIDNx0uTRynoUjIIRAgALIFStaR5YjgAAAABJRU5ErkJggg==";
// facebook icon
const FACEBOOK_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAAAYFBMVEUAAAAAQIAAWpwAX5kAX5gAX5gAX5gAXJwAXpgAWZ8AX5gAXaIAX5gAXpkAVaoAX5gAXJsAX5gAX5gAYJkAYJkAXpoAX5gAX5gAX5kAXpcAX5kAX5gAX5gAX5YAXpoAYJijtTrqAAAAIHRSTlMABFis4vv/JL0o4QvSegbnQPx8UHWwj4OUgo7Px061qCrcMv8AAAB0SURBVEjH7dK3DoAwDEVRqum9BwL//5dIscQEEjFiCPhubziTbVkc98dsx/V8UGnbIIQjXRvFQMZJCnScAR3nxQNcIqrqRqWHW8Qd6cY94oGER8STMVioZsQLLnEXw1mMr5OqFdGGS378wxgzZvwO5jiz2wFnjxABOufdfQAAAABJRU5ErkJggg==";
// whatsapp icon
const WHATSAPP_ICON = "data:image/png;base64,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";
// gplus icon
const GOOGLE_PLUS_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAACQ1BMVEUAAAD/RDP/STX9Sjb+STT+SjX+SjX+SjX+STT/SzP/Sjb/SzX/VVX/SDb+SDP+SjX9RzT9STT9SjT+STX+SjT9SjT/SST/TTP+SjX+SjX/RDP/RzP+SjX+SjX/STf9SDX/SjX/TU3+Sjb+SjX/Qyz/Szb+SjX/TTP+SjX9STX+SjP/TTX9Szb+Szb/YCD/SzX/SzX+Sjb+STX/TTX/SzX/Szb/TDT+SjX9SzX/STf+TDX/SjT9SzX9Szb+SjX/SjX/SzX/STT9SjT9TDT+SDT/VQD9STX/STX9SjX+SjX9STX+SzT/UDD9Sjb+SjX9RzT/QED+SjT+SjX/XS7+SjX/Ui7/RC3+SjX/TTz/RzP+SjX/TTP/STf+SjX/STT/RjP+Sjb/SzX/Szz/Rjr/RzL+RzP+SjX/Szf/SjX9Sjb+SjX+Sjb+SjX+SjX+SjX/STf/SjT/SjT9SjX9SzT+RzT+STT/STT+SjX/STP/Tjf+SjX/Szb/SjX/STX9SjX/SjT/AAD/SjH/STb+SzX+Sjb+SjT9SDT+Sjb+SjX9STf9STT/SDX/TDf+STb/TjT/TjH+SjX+SDT/Sjb9SzX9RzX+TDT/TUD/STX+SjX+STX/VTn/QjH/SjX+SjX/Ri7+Szb/TTP+SjX/SDX/STT9SjX+SjX/SDL/TjT9Sjb/RjL+SjX9SzX/QED/TDT+SjX+SjX9STX/RjX/VSv/Rzb/STX/ORz/UDD9SzX+Sjb/STT9SzP+SzX+SjX+SjX9Szb/Ti//ZjPPn7DtAAAAwXRSTlMAD1uiy+j5/8FBZHQDY9zvnYSc5dGhBwr+1S0Zqu44mz4KtNkXY7Yo8YLcfp3bCGZ+sLhWaks2z4wO6VOklrtWRFSXos4DoD+D/ZnoEKasjwS7+gvfHC3kHmjtMlTXYjfZXBEWa+/nQRiK5u7c8vVGRWepp6+5eulQF/dfSHSQdQEfdrzguZzm+4KSQyW1JxrAvCaCiLYUc8nGCR9h6gvzFM41MZHhYDGYTMejCEDi3osdBj1+CSCWyGyp1PC3hUEF/yhErwAAAjFJREFUSMft1tdfE0EQB/ADJD+JKAomHoqKxhJLFCnSpdgIxobYgqhYaJKIHVQUsSFiBSuCvWPv3T/N2ZPD3EucvVcyL3sz2W8+l73ZvShKKEIxcCIsPGJQpAV9MThK1KzAEAaNHjosZviI2DgBR9psVrvCx6Ni1fjRNI5JIDx2nF5m4ejxsCRqVxMmknZMksGTVUzpu5zqJD1NAodNB2boyUzCrlnK7CSKOUCyGJOC4BSan6onaWLN5irpCIwgOAMBt5eZRVk2H+fQx7n92TzK8pT8AopCwCbGgiB4Pk1fsFDPFlG2mL9gRTTdnahnxcASDx/nq6SX6tkyYLnEo1qxknBJ2t9kVSlcq2WaZM1a0qXrtOv18Jbp9Q3l5Rv/39ubHKQ3V2xRtm7bXlkluyGra2qJ76jzwb/TxH721O9K3U1fsMfsgbCXcLFZvI+wL8ok3i/6+ECDOdxYJ/TBQ9Kw+nDTkRyHtodKjjbLyGMtx304cTKi8NRpoVutfJp5xgtv21ntxGw/J7T3PNdeuAhcuqxn9o5W0p1Ma78CpF/9lzdfI3ydiStobrjhIL4BRN7k4WRa3i5D5RbQ3cPDMcDtO4ZKGXCXedtuQL1nqNwHHjDxQ/rNGYbKI/gfM/ETwv6ngafSM3RwH3O7eK86Wzz9L582PO9lN9iLl6KpXr2uf9P7tvHde4e75oNEZ3/85NQ2hKUyzg/1c57klur68vXbd9XtdP34+et36C9WKAZo/AEHHmXeIIIUCQAAAABJRU5ErkJggg==";
// email icon
const EMAIL_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAABC1BMVEUAAAA/Pz8/Pz9AQEA/Pz8/Pz8+Pj4+Pj4/Pz8/Pz8/Pz8/Pz8+Pj4+Pj4/Pz8/Pz8/Pz9AQEA+Pj5AQEA/Pz87Ozs7Ozs/Pz8+Pj47OztAQEA/Pz89PT01NTVBQUFBQUE/Pz8/Pz8+Pj4/Pz9BQUE+Pj4/Pz8/Pz89PT0+Pj4/Pz9BQUFAQEA9PT09PT0/Pz87Ozs9PT05OTk/Pz8+Pj4/Pz9AQEA/Pz8/Pz8/Pz8/Pz+AgIA+Pj4/Pz8/Pz9AQEA/Pz8/Pz8/Pz8/Pz8+Pj4/Pz8/Pz8/Pz9AQEA+Pj4/Pz8+Pj4/Pz85OTk/Pz8/Pz8/Pz8/Pz88PDw9PT0/Pz88PDw8PDw+Pj45OTlktUJVAAAAWXRSTlMA/7N4w+lCWvSx8etGX/XlnmRO7+1KY/fjOGj44DU7UvndMec/VvLbLj7YKyiJdu9O7jZ6Um1w7DnzWQJz+tpE6uY9t8D9QehAOt7PVRt5q6duEVDwSEysSPRjqHMAAAEfSURBVEjH7ZTXUgIxGEa/TwURUFyKYgMURLCvbe2gYAV7ff8nMRksgEDiKl7lXOxM5p8zO3s2CWAwGAx/CjXontzT25Y+pezxtpv2+xTygJ+BYOvh4BBDwx1lKxxhNNZqNjLK+JjVWUYsykj4+2h8gpNTUMkIBuhPNE+SKU7PQC3D62E60ziYzXIuBx0Z+XRTc9F5fgF6MhKNzWXnRejKWGJdc9GZy8AP3kyurH52Ju01XTkjvnldNN+Qi03RecthfFtPlrXz8rmzi739Ax7mUCjy6FhH/vjPonmqVD6pdT718excLX/tsItLeRAqtc7VLIsFlVy/t6+ub27v7t8XD490niy3p+rZpv3i+jy/Or+5SUrdvcNcywaDwfD/vAF2TBl+G6XvQwAAAABJRU5ErkJggg==";
// clipboard icon
const CLIPBOARD_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAAB5lBMVEUAAAA8PDw+Pj4/Pz8/Pz8/Pz8/Pz8+Pj47OzsAAAA5OTk+Pj4/Pz8/Pz8+Pj49PT0/Pz8/Pz85OTlAQEA/Pz87Ozs+Pj4+Pj4/Pz8/Pz8/Pz8zMzNBQUE/Pz8/Pz8/Pz9AQEA7Ozs9PT0/Pz9AQEA+Pj4/Pz8+Pj4AAABAQEA/Pz87OztBQUE/Pz8+Pj4zMzNDQ0M/Pz89PT03Nzc/Pz8/Pz8/Pz8/Pz88PDw8PDwAAABCQkI7Ozs9PT0/Pz9AQEA/Pz8uLi4rKytAQEA/Pz89PT0+Pj4/Pz8/Pz8/Pz9CQkJAQEA/Pz9CQkI/Pz8/Pz8/Pz8+Pj49PT0/Pz8yMjI/Pz88PDw/Pz9BQUE8PDw/Pz9AQEA/Pz8/Pz8/Pz89PT0/Pz9CQkI9PT1EREQ9PT08PDw4ODg+Pj6AgIA/Pz8/Pz82NjZVVVU7Ozs/Pz81NTVAQEA/Pz8+Pj49PT1BQUE/Pz8/Pz8/Pz8vLy8/Pz87OztAQEA3Nzc9PT0+Pj4/Pz89PT0/Pz8/Pz89PT1AQEA9PT04ODgzMzM/Pz8/Pz9AQEA/Pz9AQEA/Pz83Nzc9PT0/Pz9AQEA/Pz8+Pj4+Pj5AQEA/Pz89PT1FRUU5OTk/Pz8/Pz8+Pj47Ozs/Pz89PT08PDw+Pj6z1Mg0AAAAonRSTlMAEXTG8/7pslICKMn//J0u2LcSLNu9Y0523KoKL9b7hggauZsEOuJ/ARS7VifkiwUX0bEq1f1p6KGQAz4NpnpY8AsGtMIyb46NbSOMcRuh+fGTFc0z1yKFKy/dpKff1CqKMoYPp+lAgAKd6kIDhdorJJExNjflktMr3nkQDoXbvaCe2d2EijIUn3JsbjDDF1jjOOdWvIDhmhoJfWrAK7bYnMgx8fGWAAACNUlEQVRIx+2W6V8SURSGBxEVeydMbVER1DCwRNTCEhMNsywqExXcUrNVU9NK2wy1fd9sMyvrP+1cmYH5eK5f5f3APef85hnuvfPeM6MoaaW1dWXKMGdasrJzrJtgc7dhQ+p2kzRry4OuHfmSbEEhUTt37d5TRGNxiRRrLwUczjKKyiuI3uuSYCv3ARa3ZyOu2k/xAT5b7aXra3xaVlsH1LPZg4cAvzM10wbgMBs+QqtsDKTyJroXGz7a7AgandECtPLXfKzFY8hCbcBxFudpP3Gy49RpQ8UXtgBnOOzZc53CU+e7Ism7uYnt5ji0p1e3pDmqzTnmAEr7GGz/AGEDg0MXaBgeERXrKIWFBQz2IvlYHbtEh/EycOUqVQLXVCDPxvGz+MPYdRGWjE/coGFyyg9M32SwM8PkydlQIim7JX6DxHpvM9g7c+SjoLESmqd9vjvDYO9NEzs1aahYY7SK+3Zm31Ddmp8jDx4qysIj2qt4O6dviH4xqvk5soj40vJjqjzh7HOf6BtPtb1SnulG6X3O6bHdqb5BejHbKtDOl+UcQ78iNuwzFKKvwx1v3npYJ+kd0BYynqz3Eu2OZvnB+IyCRVE+TD5qSmWBRuDjJzb8GWhIJq4xv36kWKoH6mr1vlFDnvRW86e9Qtd/qUrs1VeKv1VKbJjrOz3Wih8UrTpF37ArMlotFmfg58raLxrjvyXfifl/ku/TdZsiK9NfNcH+y93Ed4A1JzvLkmnOMClppbV19R+iQFSQ2tNASwAAAABJRU5ErkJggg==";
// more icon
const MORE_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADwAAAA8CAMAAAANIilAAAAAQlBMVEUAAABEREQ9PT0/Pz8/Pz9AQEA7OzszMzM/Pz8/Pz9FRUU/Pz8/Pz9VVVUAAAA/Pz8+Pj4/Pz8/Pz9BQUFAQEA/Pz+e9yGtAAAAFnRSTlMAD5bv9KgaFJ/yGv+zAwGltPH9LyD5QNQoVwAAAF5JREFUSMft0EkKwCAQRFHHqEnUON3/qkmDuHMlZlVv95GCRsYAAAD+xYVU+hhprHPWjDy1koJPx+L63L5XiJQx9PQPpZiOEz3n0qs2ylZ7lkyZ9oyXzl76MAAAgD1eJM8FMZg0rF4AAAAASUVORK5CYII=";
const REACT_ICON = 'data:image/png;base64,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';
AppRegistry.registerComponent('TestShare', () => TestShare);
Tabs File
[![import React, { Component } from 'react'
import { TabBarIOS } from 'react-native'
import Share from '../components/Share'
import SaveQuote from '../components/SaveQuote'
import Home from '../containers/navRootContainer'
import MoreMenu from '../components/MoreMenu'
class Tabs extends Component {
_changeTab (i) {
const { changeTab } = this.props
changeTab(i)
}
_renderTabContent (key) {
switch (key) {
case 'home':
return <Home />
case 'share':
return <Share />
case 'savequote':
return <SaveQuote />
case 'moremenu':
return <MoreMenu />
}
}
render () {
const tabs = this.props.tabs.tabs.map((tab, i) => {
return (
<TabBarIOS.Item
key={tab.key}
icon={tab.icon}
selectedIcon={tab.selectedIcon}
title={tab.title}
onPress={() => this._changeTab(i)}
selected={this.props.tabs.index === i}>
{this._renderTabContent(tab.key)}
</TabBarIOS.Item>
)
})
return (
<TabBarIOS tintColor='white' barTintColor="black">
{tabs}
</TabBarIOS>
)
}
}
export default Tabs][2]][2]