I am trying to develop an application to learn react native and I am stuck with the login/register.
I have a register.js that works properly, as I have tested it before, and I wanted to add a resiter button to redirect to the register page from the login one.
From the UserLogin.js, the button is this one:
<TouchableOpacity
onPress={() => navigate('Register')}
style={{width:200,padding:10,backgroundColor:'pink',alignItems:'center'}}>
<Text style={{color:'black'}}>Register</Text>
</TouchableOpacity>
UserLogin.js
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,TouchableOpacity,TextInput,Button,Keyboard
} from 'react-native';
import { StackNavigator } from 'react-navigation';
class UserLogin extends React.Component {
constructor(props){
super(props)
this.state={
userEmail:'',
userPassword:''
}
}
login = () =>{
const {userEmail,userPassword} = this.state;
let reg = /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/ ;
if(userEmail==""){
//alert("Please enter Email address");
this.setState({email:'Please enter Email address'})
}
else if(reg.test(userEmail) === false)
{
//alert("Email is Not Correct");
this.setState({email:'Email is Not Correct'})
return false;
}
else if(userPassword==""){
this.setState({email:'Please enter password'})
}
else{
fetch('http://192.168.1.47/login.php',{
method:'post',
header:{
'Accept': 'application/json',
'Content-type': 'application/json'
},
body:JSON.stringify({
// we will pass our input data to server
email: userEmail,
password: userPassword
})
})
.then((response) => response.json())
.then((responseJson)=>{
if(responseJson == "ok"){
// redirect to profile page
alert("Successfully Login");
//this.props.navigation.navigate("Profile");
}else{
alert("Wrong Login Details");
}
})
.catch((error)=>{
console.error(error);
});
}
Keyboard.dismiss();
}
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
<Text style={{padding:10,margin:10,color:'red'}}>{this.state.email}</Text>
<TextInput
placeholder="Enter Email"
style={{width:200, margin:10}}
onChangeText={userEmail => this.setState({userEmail})}
/>
<TextInput
placeholder="Enter Password"
style={{width:200, margin:10}}
onChangeText={userPassword => this.setState({userPassword})}
/>
<TouchableOpacity
onPress={this.login}
style={{width:200,padding:10,backgroundColor:'magenta',alignItems:'center'}}>
<Text style={{color:'white'}}>Login</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => navigate('Register')}
style={{width:200,padding:10,backgroundColor:'pink',alignItems:'center'}}>
<Text style={{color:'black'}}>Register</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
});
export default UserLogin
AppRegistry.registerComponent('login', () => login);
register.js
import React, { Component } from 'react';
import { View, Text, StyleSheet, TextInput, TouchableOpacity, ActivityIndicator, Platform } from 'react-native';
class Register extends React.Component
{
constructor()
{
super();
this.state = { first_name: '', last_name: '', email: '', password: '', loading: false, disabled: false }
}
saveData = () =>
{
this.setState({ loading: true, disabled: true }, () =>
{
fetch('http://192.168.1.47/user_registration.php',
{
method: 'POST',
headers:
{
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(
{
first_name: this.state.first_name,
last_name: this.state.last_name,
email: this.state.email,
password: this.state.password,
})
}).then((response) => response.json()).then((responseJson) =>
{
//Faire une redirection evrs le profile de l'utilisateur
alert(responseJson);
this.setState({ loading: false, disabled: false });
}).catch((error) =>
{
console.error(error);
this.setState({ loading: false, disabled: false });
});
});
}
render()
{
return(
<View style = { styles.container }>
<TextInput underlineColorAndroid = "transparent" placeholder = "Your First Name" style = { styles.textInput } onChangeText = {(text) => this.setState({ first_name: text })}/>
<TextInput underlineColorAndroid = "transparent" placeholder = "Your Last Name" style = { styles.textInput } onChangeText = {(text) => this.setState({ last_name: text })}/>
<TextInput underlineColorAndroid = "transparent" placeholder = "Your Email adress" style = { styles.textInput } onChangeText = {(text) => this.setState({ email: text })}/>
<TextInput underlineColorAndroid = "transparent" placeholder = "Your Password" style = { styles.textInput } onChangeText = {(text) => this.setState({ password: text })}/>
<TouchableOpacity disabled = { this.state.disabled } activeOpacity = { 0.8 } style = { styles.Btn } onPress = { this.saveData }>
<Text style = { styles.btnText }>Sign up</Text>
</TouchableOpacity>
{
(this.state.loading)
?
(<ActivityIndicator size = "large" />)
:
null
}
</View>
);
}
}
const styles = StyleSheet.create(
{
container:
{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#eee',
paddingHorizontal: 25,
paddingTop: (Platform.OS == 'ios') ? 20 : 0
},
textInput:
{
height: 40,
borderWidth: 1,
borderColor: 'grey',
marginVertical: 5,
alignSelf: 'stretch',
padding: 8,
fontSize: 16
},
Btn:
{
backgroundColor: 'rgba(0,0,0,0.6)',
alignSelf: 'stretch',
padding: 10,
marginTop: 10,
marginBottom: 25
},
btnText:
{
textAlign: 'center',
color: 'white',
fontSize: 16
}
});
export default Register
Navigation.js
import Search from '../Components/Search';
import ProductDetail from '../Components/ProductDetail';
//import UserAccount from '../Components/UserAccount';
import UserLogin from '../Components/UserLogin';
//import UserRegistration from '../Components/UserRegistration';
import Transaction from '../Components/Transaction';
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import React from 'react';
import { Image , Text, TouchableOpacity } from 'react-native';
const SearchStackNavigator = createStackNavigator({
Rechercher : {
screen: Search,
navigationOptions: ({ navigate, navigation }) => ({
title:'Rechercher',
headerTitleStyle: {
fontWeight: 'bold',
color: 'white',
marginLeft:5,
},
headerStyle: {
backgroundColor: '#FF6100',
},
headerLeft: (
<TouchableOpacity
onPress={() => navigation.navigate('Rechercher')}>
<Image
style={{
width: 35,
height: 35,
marginLeft: 25,
}}
source={require('../Images/logo.jpg')}
/>
</TouchableOpacity>
),
headerRight: (
<TouchableOpacity
onPress={() => navigation.navigate('Account')}>
<Image
style={{
width: 35,
height: 35,
marginRight: 25,
}}
source={require('../Images/user.png')}
/>
</TouchableOpacity>
)
})
},
ProductDetail : {
screen: ProductDetail,
navigationOptions: ({ navigate, navigation }) => ({
title:'Détail du produit',
headerTitleStyle: {
fontWeight: 'bold',
color: 'white',
marginLeft:5,
},
headerStyle: {
backgroundColor: '#FF6100',
},
headerLeft: (
<TouchableOpacity
onPress={() => navigation.navigate('Rechercher')}>
<Image
style={{
width: 35,
height: 35,
marginLeft: 25,
}}
source={require('../Images/logo.jpg')}
/>
</TouchableOpacity>
),
headerRight: (
<TouchableOpacity
onPress={() => navigation.navigate('Account')}>
<Image
style={{
width: 35,
height: 35,
marginRight: 25,
}}
source={require('../Images/user.png')}
/>
</TouchableOpacity>
)
})
},
Account: {
//screen: UserRegistration,
screen: UserLogin,
navigationOptions: ({ navigate, navigation }) => ({
title:'Compte utilisateur',
headerTitleStyle: {
fontWeight: 'bold',
color: 'white',
marginLeft:5,
},
headerStyle: {
backgroundColor: '#FF6100',
},
headerLeft: (
<TouchableOpacity
onPress={() => navigation.navigate('Rechercher')}>
<Image
style={{
width: 35,
height: 35,
marginLeft: 25,
}}
source={require('../Images/logo.jpg')}
/>
</TouchableOpacity>
),
headerRight: (
<TouchableOpacity
onPress={() => navigation.navigate('Account')}>
<Image
style={{
width: 35,
height: 35,
marginRight: 25,
}}
source={require('../Images/user.png')}
/>
</TouchableOpacity>
)
})
},
Transaction: {
screen: Transaction,
navigationOptions: ({ navigate, navigation }) => ({
title:'Transaction',
headerTitleStyle: {
fontWeight: 'bold',
color: 'white',
marginLeft:5,
},
headerStyle: {
backgroundColor: '#FF6100',
},
headerLeft: (
<TouchableOpacity
onPress={() => navigation.navigate('Rechercher')}>
<Image
style={{
width: 35,
height: 35,
marginLeft: 25,
}}
source={require('../Images/logo.jpg')}
/>
</TouchableOpacity>
),
headerRight: (
<TouchableOpacity
onPress={() => navigation.navigate('Account')}>
<Image
style={{
width: 35,
height: 35,
marginRight: 25,
}}
source={require('../Images/user.png')}
/>
</TouchableOpacity>
)
})
},
})
export default createAppContainer(SearchStackNavigator)
Navigation.js is located on a folder named Navigation.
The two other files are on the same folder (The components one).
Can someone help me fixe it ? I don't think it's complicated, but there is something I didn't understand, I think.
Thank you in advance.
Noémie.
Your Register, and Login screens have to be listed as routes in your
stack-navigation ... so you can get access to navigation object to use
it for navigation – Hend El-Sahli
Related
I'm transitionning from React to React Native, and so far got the basics. In my application I'm trying to implement the authentification.
I already setup my working APIs for the login and registration and verified them using POSTMAN.
The problem here is for some reason onPress={onSignInHandler} doesn't do anything. Normally in React I would put onClick={onSignInHandler} and everything will be working fine.
I would like, when the Login is pressed, execute the axios request, and then redirect to for example "Myprofil.js" component or atleast display and alert to see if it's working. As far as I know React Native uses react-navigation instead of react-router.
app.js:
import * as React from 'react';
import { View, Text } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import Login from './src/views/Login';
import Signup from './src/views/Signup ';
import MyProfil from './src/views/MyProfil';
const Stack = createStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Login" screenOptions={{
headerShown: false
}}>
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Signup " component={Signup} />
<Stack.Screen name="MyProfil" component={MyProfil} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default App;
login.js :
import React, { Component, UseState } from 'react';
import { View, Text, TextInput, StyleSheet, Image, Dimensions, Alert } from 'react-native';
import axios from "axios";
const { width, height } = Dimensions.get('window');
function Login() {
const [state, setState] = React.useState({
msg: "",
isLoading: false,
redirect: false,
errMsgEmail: "",
errMsgPwd: "",
errMsg: "",
});
const [email, setEmail] = React.useState("");
const [password, setPassword] = React.useState("");
const handleChangeEmail = (event) => {
setEmail(event.target.value);
};
const handleChangePassword = (event) => {
setPassword(event.target.value);
};
function onSignInHandler() {
const infos = {
email: email,
password: password,
};
axios
.post("http://127.0.0.1:8000/api/login", infos)
.then((response) => {
if (response.data.status === 200) {
localStorage.setItem("isLoggedIn", true);
localStorage.setItem("userData", JSON.stringify(response.data.data));
this.setState({
msg: response.data.message,
redirect: true,
});
}
if (
response.data.status === "failed" &&
response.data.success === undefined
) {
this.setState({
errMsgEmail: response.data.validation_error.email,
errMsgPwd: response.data.validation_error.password,
});
setTimeout(() => {
this.setState({ errMsgEmail: "", errMsgPwd: "" });
}, 2000);
} else if (
response.data.status === "failed" &&
response.data.success === false
) {
this.setState({
errMsg: response.data.message,
});
setTimeout(() => {
this.setState({ errMsg: "" });
}, 2000);
}
})
.catch((error) => {
console.log(error);
});
};
return (
<View
style={{
flex: 1,
backgroundColor: 'white',
justifyContent: 'flex-end'
}}
>
<View style={{ ...StyleSheet.absoluteFill }}>
<Image
source={require('../images/bg.jpg')}
style={{ flex: 1, height: null, width: null }}
>
</Image>
</View>
<View style={{ height: height / 2, justifyContent: 'center' }}>
<View>
<Text style={{ fontSize: 20, fontWeight: 'bold', color:"#ffffff",fontSize: 30, marginHorizontal: 40,marginVertical: 10, }}>Bienvenue !</Text>
</View>
<View style={styles.button}>
<TextInput placeholder="E-mail " style={{ fontSize: 20, fontWeight: 'bold' }} value={email} onChange={handleChangeEmail}></TextInput>
</View>
<View style={styles.button}>
<TextInput placeholder="Password" style={{ fontSize: 20, fontWeight: 'bold' }} secureTextEntry={true} value={password} onChange={handleChangePassword}></TextInput>
</View>
<View style={{alignItems: 'center',justifyContent: 'center', marginTop: 20}}>
<Text style={{ fontSize: 20, fontWeight: 'bold', color: '#ffffff' }} onPress={onSignInHandler}>
LOGIN
</Text>
</View>
</View>
</View>
);
}
export default Login;
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
button: {
backgroundColor: 'white',
height: 50,
marginHorizontal: 30,
borderRadius: 35,
alignItems: 'center',
justifyContent: 'center',
marginVertical: 10,
}
});
Instead of using onPress for Text component wrap it with a TouchableOpacity and use the onPress of the TouchableOpacity
<TouchableOpacity onPress={onSignInHandler}>
<Text style={{ fontSize: 20, fontWeight: 'bold', color: '#ffffff' }} title="Profil" >LOGIN</Text>
</TouchableOpacity>
Change your
function onSignInHandler() {
to
const onSignInHandler = () => {
i am using redux in my react native app .
my problem is how to show the error message that appear in the terminal to the render .
i tried to use {this.props.error} and nothing will show up
Actions
import * as actionTypes from "./Types";
export const setErrors = errors => ({
type: actionTypes.SET_ERRORS,
payload: errors
});
export const resetError = () => {
return async dispatch => {
dispatch({
type: actionTypes.RESET_ERROR,
payload: []
});
};
};
Reducers
import * as actionTypes from "../actions/Types";
import { SET_ERRORS, RESET_ERROR } from "../actions/Types";
const initialState = {
errors: [],
reset: ""
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case SET_ERRORS:
console.log("from reducer");
console.log("errrrro", action.payload);
return {
...state,
errors: Object.keys(action.payload).map(
key => `${key}: ${action.payload[key]}`
)
};
case RESET_ERROR:
return {
...state,
errors: action.payload
};
default:
return state;
}
};
export default reducer;
this is my index.js
import React, { Component } from "react";
import { connect } from "react-redux";
import * as actionCreators from "../../store/actions";
import {
StyleSheet,
Text,
View,
TextInput,
Button,
TouchableHighlight,
Image
} from "react-native";
import { LinearGradient } from "expo-linear-gradient";
class Login extends Component {
state = {
username: "",
password: ""
};
resetState = () => {
this.setState({ password: null });
};
errors = () => {
this.props.errors;
};
Login = () => {
this.props.login(this.state, this.props.navigation);
this.resetState();
};
render() {
return (
<LinearGradient
colors={["#002d44", "#001724"]}
style={{ flex: 1 }}
start={[0, 0]}
end={[1, 0]}
>
<View style={styles.container}>
<Text>{error}</Text>
<View style={styles.inputContainer}>
<Image
style={styles.inputIcon}
source={{
uri:
"https://img.icons8.com/ios/80/000000/identification-documents-filled.png"
}}
/>
<TextInput
style={styles.inputs}
autoCapitalize="none"
placeholder="اسم المستخدم"
onChangeText={username => this.setState({ username })}
value={this.state.username}
/>
</View>
<View style={styles.inputContainer}>
<Image
style={styles.inputIcon}
source={{
uri: "https://img.icons8.com/ios/80/000000/lock-2-filled.png"
}}
/>
<TextInput
style={styles.inputs}
placeholder="كلمة المرور"
secureTextEntry={true}
onChangeText={password => this.setState({ password })}
value={this.state.password}
/>
</View>
<TouchableHighlight
style={[styles.buttonContainer, styles.loginButton]}
onPress={this.Login}
>
<Text style={{ color: "#fff", fontSize: 15 }}>دخول</Text>
</TouchableHighlight>
<TouchableHighlight
style={{}}
onPress={() => this.props.navigation.replace("Signup")}
>
<Text style={{ color: "#00F7EA", fontSize: 15 }}>تسجيل</Text>
</TouchableHighlight>
</View>
</LinearGradient>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center"
},
inputContainer: {
borderBottomColor: "#F5FCFF",
backgroundColor: "#FFFFFF",
borderRadius: 30,
borderBottomWidth: 1,
width: 250,
height: 45,
marginBottom: 20,
flexDirection: "row",
alignItems: "center"
},
inputs: {
height: 45,
marginLeft: 16,
fontSize: 15,
textAlign: "center",
borderBottomColor: "#FFFFFF",
flex: 1
},
inputIcon: {
width: 30,
height: 30,
marginLeft: 15,
justifyContent: "center"
},
buttonContainer: {
height: 45,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
marginBottom: 20,
width: 250,
borderRadius: 30,
borderWidth: 0.8,
borderColor: "#d6d7da"
},
loginButton: {
backgroundColor: "transparent"
},
buttonDisabled: {
backgroundColor: "#e7e7e7"
},
loginText: {
color: "white"
}
});
const mapStateToProps = state => ({
user: state.authReducer.user,
loading: state.devicesReducer.loading,
errors: state.errorReducer.errors
});
const mapDispatchToProps = dispatch => ({
login: (userData, navigation) =>
dispatch(actionCreators.login(userData, navigation)),
checkForExpiredToken: navigation =>
dispatch(actionCreators.checkForExpiredToken(navigation))
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(Login);
the error message that showing on the terminal
from reducer
errrrro Object {
"password": Array [
"This field may not be blank.",
],
"username": Array [
"This field may not be blank.",
],
}
from reducer
errrrro [Error: Request failed with status code 400]
I created a component called inputText.js, which serves to declare a particular input field according to my needs.
I use it inside login.js, passing it as Field props of redux-form.
The problem that when I begin to write even very slowly begins to start giving me problems, if I start writing faster, I get impatient, the text I write last in the field is mixed with the text already written and is found in the middle, even the stanchette that which indicates where the focus is inside the word is in the middle of the writing.
Link: Expo
Code: inputText.js
import PropTypes from 'prop-types';
import React, { Component } from 'react';
import { StyleSheet, View } from 'react-native';
import { TextInput, IconButton, Colors } from 'react-native-paper';
import Icon from 'react-native-vector-icons/MaterialIcons';
const propTypes = {
mapElement: PropTypes.func,
onSubmitEditing: PropTypes.func,
onChangeText: PropTypes.func,
value: PropTypes.string,
placeholder: PropTypes.string,
maxLength: PropTypes.number,
keyboardType: PropTypes.string,
secureTextEntry: PropTypes.bool,
label: PropTypes.string,
};
const defaultProps = {
mapElement: n => {},
onSubmitEditing: () => {},
onChangeText: () => {},
value: '',
placeholder: '',
maxLength: 200,
keyboardType: 'default',
secureTextEntry: false,
label: '',
};
const styles = StyleSheet.create({
inputBox: {
width: 300,
//backgroundColor: 'rgba(255, 255,255,0.2)',
//borderRadius: 25,
//paddingHorizontal: 16,
//fontSize: 16,
//color: '#ffffff',
//marginVertical: 10,
},
icon: {
position: 'absolute',
top: 20,
right: 10,
},
});
class InputText extends Component<{}> {
constructor(props) {
super(props);
this.state = {
vPwd: props.secureTextEntry,
};
}
changePwdType = () => {
this.setState(prevState => ({
vPwd: !prevState.vPwd,
}));
};
render() {
const {
label,
placeholder,
secureTextEntry,
iconPassword,
keyboardType,
maxLength,
value,
onChangeText,
onSubmitEditing,
} = this.props;
const { vPwd } = this.state;
let icEye = vPwd ? 'visibility-off' : 'visibility';
return (
<View>
<TextInput
style={styles.inputBox}
placeholder={placeholder}
secureTextEntry={vPwd}
keyboardType={keyboardType}
maxLength={maxLength}
returnKeyType="next"
value={value}
onSubmitEditing={onSubmitEditing}
onChangeText={onChangeText}
label={label}
underlineColor="#fff"
theme={{
colors: {
primary: '#f5a442',
background: 'transparent',
placeholder: '#fff',
},
}}
/>
{secureTextEntry && iconPassword && (
<Icon
style={styles.icon}
name={icEye}
size={25}
onPress={this.changePwdType}
/>
)}
</View>
);
}
}
InputText.defaultProps = defaultProps;
InputText.propTypes = propTypes;
export default InputText;
Code: login.js
import React, { Component } from 'react';
import { StyleSheet, View, Text, TouchableOpacity } from 'react-native';
import { Field, reduxForm } from 'redux-form';
import { HelperText } from 'react-native-paper';
import Logo from '../components/Logo';
import Form from '../components/Form';
import InputText from '../components/InputText';
import { Actions } from 'react-native-router-flux';
class Login extends Component<{}> {
signup() {
Actions.signup();
}
onSubmit = values => {
console.log(values);
};
renderTextInput = field => {
const {
meta: { touched, error },
label,
secureTextEntry,
iconPassword,
maxLength,
keyboardType,
placeholder,
input: { onChange, ...restInput },
} = field;
return (
<View>
<InputText
onChangeText={onChange}
maxLength={maxLength}
placeholder={placeholder}
keyboardType={keyboardType}
secureTextEntry={secureTextEntry}
iconPassword={iconPassword}
label={label}
{...restInput}
/>
{touched && error && (
<HelperText type="error" visible={true}>
{error}
</HelperText>
)}
</View>
);
};
render() {
const { handleSubmit } = this.props;
return (
<View style={styles.container}>
<Logo />
<View style={styles.inputContainerStyle}>
<Field
name="email"
label="Email"
placeholder="Email#host.com"
component={this.renderTextInput}
/>
<Field
name="password"
label="Password"
placeholder="***********"
secureTextEntry={true}
iconPassword={true}
component={this.renderTextInput}
/>
</View>
<TouchableOpacity
style={styles.button}
onPress={handleSubmit(this.onSubmit)}>
<Text style={styles.buttonText}>Login</Text>
</TouchableOpacity>
<View style={styles.signupTextCont}>
<Text style={styles.signupText}>Do not have an account yet?</Text>
<TouchableOpacity>
<Text style={styles.signupButton}> Signup</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#455a64',
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
signupTextCont: {
flexGrow: 1,
alignItems: 'flex-end',
justifyContent: 'center',
paddingVertical: 16,
flexDirection: 'row',
},
signupText: {
color: 'rgba(255,255,255,0.6)',
fontSize: 16,
},
signupButton: {
color: '#ffffff',
fontSize: 16,
fontWeight: '500',
},
button: {
width: 300,
backgroundColor: '#1c313a',
borderRadius: 25,
marginVertical: 10,
paddingVertical: 13,
},
buttonText: {
fontSize: 16,
fontWeight: '500',
color: '#ffffff',
textAlign: 'center',
},
inputContainerStyle: {
backgroundColor: 'rgba(255, 255, 255, 0.15)',
padding: 5,
paddingBottom: 10,
borderRadius: 5,
},
});
const validate = values => {
const errors = {};
if (!values.email) {
errors.email = 'Email is required';
}
if (!values.password) {
errors.password = 'Password is required';
}
return errors;
};
export default reduxForm({
form: 'login',
validate,
})(Login);
I am trying to render either an image profile from firebase storage or a default avatar which is in my project img folder, depending on whether or not the current user has a profile or not. While the following code displays either the user image or the avatar image perfectly fine in my Menu.js :
import React, { Component } from 'react';
import { View, Text, Image } from 'react-native';
import { Actions } from 'react-native-router-flux';
import { Content, List, ListItem, Thumbnail } from 'native-base';
import firebase from 'firebase';
import Helpers from '../../lib/helpers';
class Menu extends Component {
constructor(props) {
super(props);
this.state = {
avatarUrl: '',
userName: '',
userLastname: '',
userEmail: '',
userBio: '',
uid: ''
};
}
async componentWillMount() {
try {
const user = await firebase.auth().currentUser;
Helpers.getImageUrl(user.uid, (imageUrl) => {
this.setState({ avatarUrl: imageUrl });
});
Helpers.getUserName(user.uid, (name) => {
this.setState({ userName: name });
});
Helpers.getUserLastname(user.uid, (lastname) => {
this.setState({ userLastname: lastname });
});
Helpers.getUserEmail(user.uid, (email) => {
this.setState({ userEmail: email });
});
Helpers.getUserBio(user.uid, (bio) => {
this.setState({ userBio: bio });
});
this.setState({ uid: user.uid });
} catch (error) {
console.log(error);
}
}
mostrarAvatar() {
if (this.state.avatarUrl) {
return (<Image
style={{ width: 100, height: 100, borderRadius: 150 / 2 }}
source={{ uri: this.state.avatarUrl }}
/>);
}
return (<Thumbnail
source={require('../../img/avatar.png')}
large
/>);
}
mostrarNombreUsuario() {
if (this.state.userName !== '') {
return <Text style={{ color: 'white', fontSize: 15 }}>{this.state.userName}</Text>;
}
return <Text style={{ color: 'white', fontSize: 15 }}>Usuario</Text>;
}
render() {
const { userName, userLastname, userEmail, userBio, avatarUrl } = this.state;
return (
<View style={{ flex: 1 }}>
<View style={{ flex: 1, backgroundColor: '#2c3e50', justifyContent: 'center', alignItems: 'center' }}>
{this.mostrarAvatar()}
{this.mostrarNombreUsuario()}
</View>
<View style={{ flex: 2 }}>
<Content>
<List>
<ListItem onPress={() => Actions.albumList()}>
<Text>Los más escuchados</Text>
</ListItem>
<ListItem>
<Text>buscar por género</Text>
</ListItem>
<ListItem onPress={() => Actions.profile({
name: userName,
lastname: userLastname,
email: userEmail,
bio: userBio,
avatar: avatarUrl })}
>
<Text>Perfil</Text>
</ListItem>
<ListItem onPress={() => Actions.auth()}>
<Text>Cerrar sesión</Text>
</ListItem>
</List>
</Content>
</View>
</View>
);
}
}
export default Menu;
The one in my Profile.js is not: (see renderAvatar function)
import React, { Component } from 'react';
import _ from 'lodash';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
Image,
TextInput,
Platform,
ToastAndroid,
Dimensions,
ScrollView,
Thumbnail
} from 'react-native';
import ImagePicker from 'react-native-image-picker';
import firebase from 'firebase';
import RNFetchBlob from 'react-native-fetch-blob';
import Helpers from '../../lib/helpers';
import Avatar from '../../img/avatar.png';
const ITEM_WIDTH = Dimensions.get('window').width;
const Blob = RNFetchBlob.polyfill.Blob;
const fs = RNFetchBlob.fs;
window.XMLHttpRequest = RNFetchBlob.polyfill.XMLHttpRequest;
window.Blob = Blob;
const uploadImage = (uri, imageName, mime = 'image/jpg') => {
return new Promise((resolve, reject) => {
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri;
let uploadBlob = null;
const imageRef = firebase.storage().ref('images').child(imageName);
fs.readFile(uploadUri, 'base64')
.then((data) => {
return Blob.build(data, { type: `${mime};BASE64` });
})
.then((blob) => {
uploadBlob = blob;
return imageRef.put(blob, { contentType: mime });
})
.then(() => {
uploadBlob.close();
return imageRef.getDownloadURL();
})
.then((url) => {
resolve(url);
})
.catch((error) => {
reject(error);
});
});
};
class Profile extends Component {
constructor(props) {
super(props);
this.state = {
imagePath: '',
name: this.props.avatar !== 'profile' ? this.props.name : '',
lastname: this.props.avatar !== 'profile' ? this.props.lastname : '',
email: this.props.avatar !== 'profile' ? this.props.email : '',
bio: this.props.avatar !== 'profile' ? this.props.bio : '',
uid: ''
};
}
async componentWillMount() {
try {
const { currentUser } = await firebase.auth();
this.setState({
uid: currentUser.uid
});
} catch (error) {
console.log(error);
}
}
openImagePicker() {
const options = {
title: 'Seleccione avatar',
storageOptions: {
skipBackup: true,
path: 'images'
}
};
ImagePicker.showImagePicker(options, (response) => {
if (response.didCancel) {
console.log('User canceled image Picker');
} else if (response.error) {
console.log('Error' + response.error);
} else if (response.customButton) {
console.log('User tapped custom button' + response.customButton);
} else {
this.setState({
imagePath: response.uri,
imageHeight: response.height,
imageWidth: response.width
});
}
});
}
saveForm() {
const { name, lastname, email, bio, uid, imagePath } = this.state;
if (uid) {
try {
name ? Helpers.setUserName(uid, name) : null;
bio ? Helpers.setUserBio(uid, bio) : null;
email ? Helpers.setUserEmail(uid, email) : null;
lastname ? Helpers.setUserLastname(uid, lastname) : null;
imagePath ?
uploadImage(imagePath, `${uid}.jpg`)
.then((responseData) => {
Helpers.setImageUrl(uid, responseData);
})
.done()
: null;
toastMessage('¡Tu datos han sido actualizados!');
} catch (error) {
console.log(error);
}
}
}
renderAvatar() {
console.log(this.props.avatar);
if (this.props.avatar !== null) {
return (<Image
style={{ width: 100, height: 100, borderRadius: 150 / 2 }}
source={{ uri: this.props.avatar }}
/>);
}
return (<Image
style={{ width: 100, height: 100, borderRadius: 150 / 2 }}
source={Avatar}
/>);
}
render() {
return (
<ScrollView contentContainerStyle={{ alignItems: 'center' }} style={styles.container}>
<TouchableOpacity
onPress={this.openImagePicker.bind(this)}
style={{ marginBottom: 40, marginTop: 20 }}
>
<View style={{ alignItems: 'center' }}>
{this.renderAvatar()}
<Text style={{ color: 'white' }}>Avatar </Text>
</View>
</TouchableOpacity>
<TextInput
underlineColorAndroid='rgba(0,0,0,0)'
selectionColor="#fff"
placeholderTextColor="#ffffff"
autoCorrect={false} //we disable the autocorrect from ios or android
style={styles.textInput}
placeholder='nombres'
value={this.state.name}
onChangeText={(name) => this.setState({ name })}
/>
<TextInput
underlineColorAndroid='rgba(0,0,0,0)'
selectionColor="#fff"
placeholderTextColor="#ffffff"
autoCorrect={false} //we disable the autocorrect from ios or android
style={styles.textInput}
placeholder='apellidos'
value={this.state.lastname}
onChangeText={(lastname) => this.setState({ lastname })}
/>
<TextInput
underlineColorAndroid='rgba(0,0,0,0)'
selectionColor="#fff"
placeholderTextColor="#ffffff"
autoCorrect={false} //we disable the autocorrect from ios or android
style={styles.textInput}
type='email-address'
placeholder='email#gmail.com'
keyboardType="email-address"
autoCapitalize="none"
value={this.state.email}
onChangeText={(email) => this.setState({ email })}
/>
<TextInput
multiline={true}
numberOfLines={10}
underlineColorAndroid='rgba(0,0,0,0)'
selectionColor="#fff"
placeholderTextColor="#ffffff"
autoCorrect={false} //we disable the autocorrect from ios or android
style={styles.textArea}
placeholder='Ingresa algo sobre tí'
value={this.state.bio}
onChangeText={(bio) => this.setState({ bio })}
/>
<TouchableOpacity
style={styles.buttonStyle}
onPress={this.saveForm.bind(this)}
>
<Text style={styles.buttonText}>Guardar</Text>
</TouchableOpacity>
</ScrollView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#0277BD',
},
textInput: {
height: 40,
borderWidth: 1,
borderColor: '#cecece',
marginBottom: 20,
borderRadius: 20,
paddingHorizontal: 20,
marginHorizontal: 50,
backgroundColor: 'rgba(255, 255, 255, 0.3)',
color: '#ffffff',
width: ITEM_WIDTH - 100,
},
buttonStyle: {
justifyContent: 'center',
alignItems: 'center',
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 20,
paddingVertical: 10,
width: 100,
backgroundColor: '#2D09A1',
marginHorizontal: 20,
marginTop: 20,
marginBottom: 20
},
buttonText: {
color: 'white',
fontSize: 16,
fontWeight: '600',
},
textArea: {
height: 150,
justifyContent: 'flex-start',
textAlignVertical: 'top',
borderWidth: 1,
borderColor: '#cecece',
marginBottom: 20,
borderRadius: 20,
paddingHorizontal: 16,
backgroundColor: 'rgba(255, 255, 255, 0.3)',
color: '#ffffff',
width: ITEM_WIDTH - 150,
}
});
const toastMessage = (texto) => {
ToastAndroid.showWithGravity(
texto,
ToastAndroid.SHORT,
ToastAndroid.CENTER
);
};
export default Profile;
The problem is in the renderAvatar method: I have defined the method in the same way that I did in the Menu.js and called it in the return the same way I did in the Menu.js and used the same image path as in the Menu.js, and both the Menu.js and Profile.js are located in the same directory. But I get the following error: 'uri should not be an empty string'.
When I just return:
render() {
return (
<ScrollView contentContainerStyle={{ alignItems: 'center' }} style={styles.container}>
<TouchableOpacity
onPress={this.openImagePicker.bind(this)}
style={{ marginBottom: 40, marginTop: 20 }}
>
<View style={{ alignItems: 'center' }}>
<Image
style={{ width: 100, height: 100, borderRadius: 150 / 2 }}
source={Avatar}
/> ...
in my Profile.js the default avatar image displays correctly. I don't get this. I don't get the logic of this. If anybody can help me I would appreciate it. Thanks.
In the constructor you can not use this.props. you have to use props directly because this is not available at that moment. Let me see your code when you use the Profile component.
I think you have to compare this.props.avatar with '' not with null because your initial state.
<ListItem onPress={() => Actions.profile({
name: userName,
lastname: userLastname,
email: userEmail,
bio: userBio,
avatar: avatarUrl })}
>
you are passing '' not null in avatarUrl value. Also you can initialize the avatarUrl state with the uri value of the default avatar you have until this change, because of this your Profile component always have a valid url for the image component.
I'm working on a react native application, and using a DrawerNavigator. Unfortunately I'm not able to change the colour of the header, my SideMenu component looks like this:
import React from 'react';
import {
TouchableHighlight,
View,
ScrollView,
Image,
Platform,
StyleSheet
} from 'react-native';
import {NavigationActions} from 'react-navigation';
import {
RkStyleSheet,
RkText,
RkTheme
} from 'react-native-ui-kitten';
import { MainRoutes } from '../routes/routes';
import { MaterialCommunityIcons } from 'react-native-vector-icons';
import { connect } from 'react-redux';
const mapStateToProps = (state) => {
return {
}
}
class SideMenu extends React.Component {
static navigationOptions = ({navigation}) => {
const { state, setParams } = navigation;
return {
headerTintColor: 'red',
headerLeft: null,
headerStyle: { backgroundColor: 'rgba(77, 90, 139, 1)', shadowColor: 'transparent', borderBottomWidth: 0},
};
};
constructor(props) {
super(props);
this._navigateAction = this._navigate.bind(this);
}
_navigate(route) {
let resetAction = NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({routeName: route.id})
]
});
this.props.navigation.dispatch(resetAction)
}
_renderIcon() {
// if (RkTheme.current.name === 'light')
// return <Image style={styles.icon} source={require('../../assets/images/smallLogo.png')}/>;
// return <Image style={styles.icon} source={require('../../assets/images/smallLogoDark.png')}/>
}
handlePress = (route, index) => {
const { navigation } = this.props;
navigation.navigate(route.id);
}
render() {
let menu = MainRoutes.map((route, index) => {
return (
<TouchableHighlight
style={styles.container}
key={route.id}
underlayColor={RkTheme.current.colors.button.underlay}
activeOpacity={1}
onPress={() => this.handlePress(route, index)}>
<View style={styles.content}>
<View style={styles.content}>
<MaterialCommunityIcons size={25} style={{color: 'white'}} name={route.icon} />
<View style={{flex: 1, alignItems: 'left', paddingLeft: 15}}>
<RkText style={{color:'white'}}>{route.title}</RkText>
</View>
</View>
</View>
</TouchableHighlight>
)
});
return (
<View style={styles.root}>
<ScrollView
showsVerticalScrollIndicator={false}>
<View style={[styles.container, styles.content], {borderTopWidth: 0}}>
{this._renderIcon()}
</View>
{menu}
</ScrollView>
</View>
)
}
}
let styles = RkStyleSheet.create(theme => ({
container: {
height: 80,
paddingHorizontal: 16,
borderTopWidth: StyleSheet.hairlineWidth,
borderColor: 'white',
backgroundColor: 'rgba(77, 90, 139, 1)'
},
root: {
paddingTop: Platform.OS === 'ios' ? 20 : 0,
backgroundColor: 'rgba(77, 90, 139, 1)'
},
content: {
flex: 1,
flexDirection: 'row',
alignItems: 'center'
},
icon: {
marginRight: 13,
}
}));
export default connect(mapStateToProps)(SideMenu);
As you can see I do set the style of the header in the NavigationOptions like I do with the other components but the header stays the same colour.
Could this be because the DrawerNavigator is nested within a TabNavigator?
Thanks and really appreciate any help.
The Navigators are defined as so:
onst SettingsDrawerNavigator = DrawerNavigator(
{
SettingsScreen: {
screen: SettingsScreen
}
},
{
//initialRouteName: 'SettingsScreen',
drawerOpenRoute: 'DrawerOpen',
drawerCloseRoute: 'DrawerClose',
drawerToggleRoute: 'DrawerToggle',
contentComponent: (props) => <SideMenu {...props}/>
}
);
export default TabNavigator(
//Adds elements to the navigator at the bottom.
{
//Other tabs.
Account: {
screen: SettingsDrawerNavigator,
}
},
{
navigationOptions: ({ navigation }) => ({
initialRouteName: 'Home',
tabBarIcon: ({ focused }) => {
const { routeName } = navigation.state;
let iconName;
return (
// <Button badge vertical>
// <Badge ><Text>51</Text></Badge>
<Ionicons
name={iconName}
size={24}
style={{ marginBottom: -3 }}
color={focused ? Colors.tabIconSelected : Colors.tabIconDefault}
/>
// </Button>
);
},
}),
tabBarOptions: {
inactiveBackgroundColor: 'transparent',
activeBackgroundColor: 'transparent',
showLabel: false,
style: {
backgroundColor: '#4d5a8b',
}
},
tabBarComponent: TabBarBottom,
tabBarPosition: 'bottom',
animationEnabled: false,
swipeEnabled: false
}
);
Adding a header to the DrawerNavigator resulted in the following (red). I'm trying to set the white background at the top to red.