App.js
import React, { Component } from 'react';
import UserApp from './MainApplication';
import store from './store/index';
import { Provider } from 'react-redux';
class App extends Component {
render() {
return (
<Provider store={store}>
<UserApp />
</Provider>
);
}
}
reducer:
import { ADD_USER } from '../actions/actionTypes';
// import { initialState } from '../store/initialState';
const initialState = {
id: 0,
username: 'test',
// email: '',
// password: '',
// otp: false,
// otpColor: 'red',
// otpStatus: 'Send OTP',
};
const addUser = (state = initialState, action) => {
switch (action.type) {
case ADD_USER:
return Object.assign({}, state, {
//id: action.payload.id,
username: action.data,
// email: action.email,
// password: action.password,
// otp: action.otp,
// otpColor: action.otpColor,
// otpStatus: action.otpStatus,
});
}
return state;
};
export default addUser;
export default App;
actionCreator:
import { ADD_USER, ADD_IMAGE, UPDATE_API, SEND_OTP } from './actionTypes';
let nextID = 0;
export const addUser = (data) => ({
type: ADD_USER,
id: (nextID += 1),
data,
});
component:
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
Image,
Button,
TextInput,
} from 'react-native';
import { ImagePicker, Permissions } from 'expo';
// import Icon from 'react-native-vector-icons/Ionicons';
import { connect } from 'react-redux';
class Users extends Component<Props> {
constructor(props) {
super(props);
this.state = {
image: null,
};
}
_pickImage = async () => {
const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
if (status === 'granted') {
let result = await ImagePicker.launchImageLibraryAsync({
allowsEditing: true,
aspect: [4, 3],
});
console.log(result);
if (!result.cancelled) {
this.setState({ image: result.uri });
}
} else {
throw new Error('Camera roll permission not granted');
}
};
render() {
//const { fullName, email, password } = this.props.navigation.state.params;
let { image } = this.state;
return (
<View style={styles.mainContainer}>
<View styles={styles.container}>
<TouchableOpacity style={styles.imageContainer}>
image &&
<Image source={{ uri: image }} style={styles.image} />
</TouchableOpacity>
<Button
title={
this.state.image === null
? 'Pick an image from camera roll'
: 'Change Image'
}
onPress={this._pickImage}
/>
<View>
<Text style={styles.label}>Full Name: </Text>{' '}
<TextInput
placeholder={this.props.username}
//onChangeText={email => this.setState({ email })}
style={styles.input}
returnKeyType="next"
autoCapitalize="none"
autoCorrect={false}
/>
<Text style={styles.label}>Email: </Text>
<TextInput
placeholder={this.props.email}
//onChangeText={email => this.setState({ email })}
style={styles.input}
returnKeyType="next"
autoCapitalize="none"
autoCorrect={false}
/>
</View>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('Home')}
style={styles.btnContainer}>
<Text style={styles.btn}>FINISH</Text>
</TouchableOpacity>
</View>
<View>
<Text>username: {this.props.username}</Text>
</View>
</View>
);
}
}
const mapStateToProps = state => ({
username: state.username
});
export default connect(mapStateToProps)(Users);
desired Component:
import React, { Component } from 'react';
import {
View,
StyleSheet,
Text,
TextInput,
KeyboardAvoidingView,
TouchableOpacity,
ScrollView,
} from 'react-native';
import { connect } from 'react-redux';
// import { addUser } from '../reducers/addUser';
import { addUser } from '../actions';
import { ADD_USER } from '../actions/actionTypes';
class SignUp extends Component<Props> {
constructor(props) {
super(props);
// local state..
this.state = {
username: '',
email: '',
password: '',
otp: false,
otpColor: 'red',
otpStatus: 'Send OTP',
};
}
// addUser = (username) => {
// this.props.dispatch({ type: ADD_USER, username })
// }
render() {
//const { navigate } = this.props.navigation;
//const newUser = this.state;
return (
<View style={styles.mainContainer}>
<View style={styles.header}>
<View style={styles.createAccountContainer}>
<Text style={styles.createAccount}> Create an account </Text>
<View style={styles.underLineRight} />
</View>
<View style={styles.singInContainer}>
{' '}
<TouchableOpacity
onPress={() => this.props.navigation.navigate('Login')}>
<Text style={styles.signupHeader}> Sign In </Text>
</TouchableOpacity>
<View style={styles.underLineLeft} />
</View>
</View>
<View style={styles.content}>
<TextInput
placeholder="FULL NAME"
value={this.state.username}
onChangeText={username => this.setState({ username })}
// onSubmitEditing={() => this.newUser.email.focus()}
style={styles.input}
returnKeyType="next"
autoCapitalize="none"
autoCorrect={false}
/>
<View style={styles.emailContainer}>
<TextInput
placeholder="EMAIL"
value={this.state.email}
onChangeText={email => this.setState({ email })}
//onSubmitEditing={() => this.newUser.password.focus()}
keyboardType="email-address"
style={[styles.input, { flex: 2 }]}
returnKeyType="next"
autoCapitalize="none"
autoCorrect={false}
/>
<TouchableOpacity
value={this.state.email}
onPress={() => {
this.setState({
otp: true,
otpStatus: 'OTP Send',
otpColor: 'green',
});
}}
style={styles.otpInput}>
<Text style={{ color: this.state.otpColor }}>
{this.props.otpStatus}
</Text>
</TouchableOpacity>
</View>
<TextInput
placeholder="PASSWORD"
value={this.state.password}
//onChangeText={password => this.setState({ password })}
style={styles.input}
secureTextEntry={true}
returnKeyType="go"
/>
<TouchableOpacity
onPress={() => {
console.log(
this.props.username + 'removeID TouchableOpacity(104)-- Users.js'
);
if (this.props.addNewUser()) this.props.navigation.navigate('Users');
}}
// onPress={() =>
// navigate('Users', {
// username: username,
// email: email,
// password: password,
// })
// }
style={styles.btnContainer}>
<Text style={styles.btn}>Sign up</Text>
</TouchableOpacity>
</View>
<View style={styles.orContainer}>
<Text style={{ color: 'grey' }}>─────</Text>
<Text style={styles.or}>Or</Text>
<Text style={{ color: 'grey' }}>─────</Text>
</View>
<View style={styles.footer}>
<TouchableOpacity style={styles.labelsGoogle}>
<Text style={styles.btn}>google</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.labelfacebook}>
<Text style={styles.btn}>facebook</Text>
</TouchableOpacity>
</View>
<View>
<Text>username: {this.props.username}</Text>
</View>
</View>
);
}
}
// Add to the props of this class..
const mapStateToProps = state => {
console.log(state);
return {
username: state.username,
// email: state.email,
// otp: state.otp,
// otpColor: state.otpColor,
// otpStatus: state.otpStatus,
};
};
// Add to the props of this class..
const mapDispatchToProps = (dispatch) => ({
addNewUser: username => dispatch(addUser(username)),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(SignUp);
problems are,
cannot map my initial state to the component.
not able to update the state.
i don't have a senior to ask and i'm a beginner. done a lot of surfing. the proj was working properly till converted to redux. require help. thanks in advance..
First of all, You can call action method in componenet and
import { addUser } from '../actions'; // import
this.props.addUser(); // call method
export default connect(mapStateToProps, { addUser })(Users); // connect actions method
Than you can set the reducers file in bellow
export default (state = initialState, action) => {
switch (action.type) {
case ADD_USER:
return Object.assign({}, state, {
//id: action.payload.id,
username: action.data,
// email: action.email,
// password: action.password,
// otp: action.otp,
// otpColor: action.otpColor,
// otpStatus: action.otpStatus,
});
default:
return state;
}
};
Related
here my context file
import React from 'react';
import storage from '#helper/storage/user-storage';
export const AuthContext = React.createContext();
class AuthContextProvider extends React.Component{
state = {
user: null,
nigth_mode: false,
token: null
}
login = async ({user, token}) => {
await storage.storeUser(user)
await storage.storeToken(token)
this.setState({
user: user,
token: token
})
}
logout = async () => {
storage.removeAll().then(() => {
this.setState({
user: null,
token: null
})
});
}
setUser = async (data) => {
this.setState({
user: data
})
}
nigth_mode = async () => {
this.setState({
nigth_mode: !nigth_mode
})
}
render(){
return(
<AuthContext.Provider
value={{
...this.state,
login: this.login,
logout: this.logout,
setUser: this.setUser,
nigth_mode: this.nigth_mode,
token: this.state.token
}}
>
{ this.props.children }
</AuthContext.Provider>
);
}
}
export default AuthContextProvider
in my login file i have this render
render(){
return(
<AuthContext.Consumer>
{context => {
console.log('token', context)
if(context.token){
// this.props.navigation.reset({
// index: 0,
// routes: [{ name: 'Home' }],
// });
this.props.navigation.navigate('View')
}else{
return(
<LinearGradient
colors={['#232526', '#182848']}
style={styles.mainContainerLogin}>
<KeyboardAvoidingView
style={styles.mainContainerLogin}
behavior={'height'}
>
{/* <View style={styles.loginLogo}>
<Image
source={require('../Images/ic-logo.jpg')}
style={styles.logo}
/>
</View> */}
<View style={styles.inputContainer}>
<Ionicon
name="md-person"
size={20}
color="rgb(110,182,245)"
style={styles.inputIcon}
/>
<TextInput
placeholder={'Email'}
placeholderTextColor={'rgba(255,255,255,0.5)'}
underlineColorAndroid="transparent"
style={styles.loginInput}
onChangeText={value => (this.userref = value)}
/>
</View>
<View style={styles.inputContainer}>
<Ionicon
name="md-lock-closed"
size={20}
color="rgb(110,182,245)"
style={styles.inputIcon}
/>
<TextInput
placeholder="Password"
placeholderTextColor={'rgba(255,255,255,0.5)'}
underlineColorAndroid="transparent"
secureTextEntry={this.state.keyHide}
style={styles.loginInput}
onChangeText={value => (this.password = value)}
/>
<TouchableOpacity
style={styles.btnEye}
onPress={this._showPassword.bind(this)}>
<Ionicon
name={
this.state.press == false ? 'md-eye' : 'md-eye-off'
}
size={20}
color="rgb(110,182,245)"
/>
</TouchableOpacity>
</View>
{this._displayLoading()}
{_errorTextLogin(this.state.error)}
</KeyboardAvoidingView>
</LinearGradient>
);
}
}}
</AuthContext.Consumer>
);
so on clicking login button, the context.login is called, so it's set the state of token, so in my condition if my context.token exist i navigate to another page; if i console log on context.token, i got eh token when login is success,then the navigation is processed successfuly but i got a error popup on the new page
Warning: Cannot update a component (`ForwardRef(BaseNavigationContainer)`) while rendering a different component (`Context.consumer`)
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.
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
I have the following script. It's not quite there yet but I'm stuck on getting the class to render
Here's where I think I'm messing up:
{this.state.todos.map(todo=>{
<Todo
onToggle={()=>(this.toggle(todo.id))}
onDelete={()=>(this.removeTodo(todo.id))}
todo={todo}
key={todo.id}
/>})}
and here's the entire code:
import React from 'react';
import { TextInput,Button, StyleSheet, View,Text, ScrollView } from 'react-native';
import {Constants} from 'expo'
let id=0
{/* const Todo = (props) => (
<Text>
{/* <input type='checkbox'
checked={props.todo.checked}
onClick={props.onToggle}
/>
<Button title='delete' button onPress={props.onDelete}></Button>
<Text>{props.todo.text}</Text>
</Text>
) */}
class Todo extends React.Component{
shouldComponentUpdate(nextProps,nextState){
return true
}
render(){
console.log('inside')
return(
<Text>
<Button title='delete' button onPress={props.onDelete}></Button>
<Text>{props.todo.text}</Text>
</Text>
)
}}
export default class App extends React.Component {
constructor(){
super()
this.state={
todos:[],
inputText:'',
update:false
}
}
clearText(){
this.setState({inputText:''})
this.setState({update:true})
}
addTodo(text){
this.setState({update:false})
this.setState({todos: [...this.state.todos,
{ id:id++,
text: text,
checked:false
}
]
})
this.setState({inputText:text})
}
toggle(id){
this.setState({todos: this.state.todos.map(todo=>{
if(id!==todo.id)return todo
return{
id:todo.id,
text:todo.text,
checked: !todo.checked}})})
}
removeTodo(id){
this.setState({todos: this.state.todos.filter(todo=>(todo.id!==id))})
}
render(){
return(
<View style={styles.container}>
<Text >Count of Todos: {this.state.todos.length}</Text>
<Text >{"Todo's checked:"}
{this.state.todos.filter(todo =>(todo.checked===true)).length}</Text>
<TextInput
style={{height:25,borderColor:'red',borderWidth:1,textAlign:'center'}}
value={this.state.inputText}
placeholder={'add Todo'}
onSubmitEditing={()=>{this.clearText()}}
onChangeText={(text) => {this.addTodo(text)}}
/>
<ScrollView>
{this.state.todos.map(todo=>{
<Todo
onToggle={()=>(this.toggle(todo.id))}
onDelete={()=>(this.removeTodo(todo.id))}
todo={todo}
key={todo.id}
/>})}
</ScrollView>
</View>
)
}
}
const styles = StyleSheet.create({
container:{
flex:1,
flexDirection:'column',
height:50,
paddingTop:3*Constants.statusBarHeight,
}
})
You need to return the data from the map in order to render it.
{this.state.todos.map(todo=> (
<Todo
onToggle={()=>(this.toggle(todo.id))}
onDelete={()=>(this.removeTodo(todo.id))}
todo={todo}
key={todo.id}
/>))}
=> {} returns undefined since it is an explicit block, you need to return an implicit block
Here is working code. I modified your code to run on my pc.
import React from 'react';
import {TextInput, Button, StyleSheet, View, Text, ScrollView} from 'react-native';
//import {Constants} from 'expo'
let id = 0
{/* const Todo = (props) => (
<Text>
{/* <input type='checkbox'
checked={props.todo.checked}
onClick={props.onToggle}
/>
<Button title='delete' button onPress={props.onDelete}></Button>
<Text>{props.todo.text}</Text>
</Text>
) */}
class Todo extends React.Component {
shouldComponentUpdate (nextProps, nextState) {
return true
}
render () {
console.log('inside')
return (
<Text>
<Button title='delete' onPress={this.props.onDelete}></Button>
<Text>{this.props.todo.text}</Text>
</Text>
)
}
}
export default class App extends React.Component {
constructor () {
super()
this.state = {
todos: [],
inputText: '',
update: false
}
}
clearText () {
this.setState({inputText: ''})
this.setState({update: true})
}
addTodo (text) {
this.setState({update: false})
this.setState({
todos: [...this.state.todos,
{
id: id++,
text: text,
checked: false
}
]
})
this.setState({inputText: text})
}
toggle (id) {
this.setState({
todos: this.state.todos.map(todo => {
if (id !== todo.id) return todo
return {
id: todo.id,
text: todo.text,
checked: !todo.checked
}
})
})
}
removeTodo (id) {
this.setState({todos: this.state.todos.filter(todo => (todo.id !== id))})
}
render () {
return (
<View style={styles.container}>
<Text >Count of Todos: {this.state.todos.length}</Text>
<Text >{"Todo's checked:"}
{this.state.todos.filter(todo => (todo.checked === true)).length}</Text>
<TextInput
style={{height: 25, borderColor: 'red', borderWidth: 1, textAlign: 'center'}}
value={this.state.inputText}
placeholder={'add Todo'}
onSubmitEditing={() => {this.clearText()}}
onChangeText={(text) => {this.addTodo(text)}}
/>
<ScrollView>
{this.state.todos.map(todo =>
<Todo
onToggle={() => (this.toggle(todo.id))}
onDelete={() => (this.removeTodo(todo.id))}
todo={todo}
key={todo.id}
/>
)}
</ScrollView>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
height: 50,
//paddingTop:3*Constants.statusBarHeight,
paddingTop: 3 * 10,
}
})
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]