I'm trying to make a basic login screen. However, I'm struggling with the e-mail validation.
Every time I press a button, I want to make sure that the user inputs the correct e-mail format, or give them a small alert under the e-mail TextInput if they write something other than a valid e-mail (this alert will be hidden by default.). However, when I try, it always gives me this error (regardless of the user input):
undefined is not an object (evaluating 'this.state.username')
This is my code so far
class HomeScreen extends Component {
state = { username: null, password: null }
show = false;
_validar = (text) => {
let reg = /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/ ;
if(reg.test(text) === false) {
return false;
} else {
return true;
}
}
_onSubmit() {
if(this._validar(this.state.username) == true) {
const { username, password } = this.state;
} else {
this.show = true;
}
}
render() {
return (
<KeyboardAwareScrollView contentContainerStyle={styles.container} scrollEnabled
enableOnAndroid={true} resetScrollToCoords={{x:0, y:0}}>
<View style={styles.logo}>
<Image source = {logo} style={styles.img}/>
<Text style={styles.textLogoPrimary}>Neuron App</Text>
<Text style={styles.textLogoSecondary}>Test</Text>
</View>
<View style={styles.formElement}>
<Text style={styles.formText}>Correo Electrónico</Text>
<TextInput keyboardType='email-address' placeholder='Email' onChangeText={value => this.setState({ username: value })}
style={styles.formInput} />
{this.state.show ? (
<Text style={styles.textAlert}>Correo Electrónico no valido.</Text>
) : null}
</View>
<View style={styles.formElement}>
<Text style={styles.formText}>Contraseña</Text>
<TextInput style={styles.formInput} onChangeText={value => this.setState({ password: value })} secureTextEntry={true}/>
</View>
<View style={styles.buttonView}>
<TouchableOpacity style={styles.button} onPress={this._onSubmit}>
<Text style={styles.buttonText}>Iniciar</Text>
</TouchableOpacity>
</View>
</KeyboardAwareScrollView>
);
}}
Just change your onSubmit funciton to "
_onSubmit = () =>{
if(this._validar(this.state.username) == true) {
const { username, password } = this.state;
} else {
this.show = true;
}
}
ES^ binds this automatically. Hope it solves the error
This error is raised because you are accessing the this.state.username before it is assigned. I would recommend you to change the initial state value of username into a string, as it ends up being a string, instead of null.
state = { username: '', password: '' };
null can be avoided in string case, by just assigning it '' an empty string. If you are to evaluate it logically, it would give you same result as null as it is 'falsy'.
Related
I have a quiz written in React Native. When the user presses the right answer, I want the button to become green before moving to the next question. If it's wrong, I want the right answer to become green and the pressed button to become red. I'm quite new to React and not sure how to change state of only a particular button. How it looks now, all buttons becomes red/green as I set the background color for all the buttons.
Quiz screen:
state = {
correctCount: 0,
totalCount: this.props.navigation.getParam("questions", []).length,
activeQuestionIndex: 0,
answered: false,
answerCorrect: false,
btnColor: {backgroundColor: '#FFDD7C'}
};
answer = correct => {
this.setState(
state => {
const nextState = { answered: true };
if (correct) {
nextState.correctCount = state.correctCount + 1;
nextState.answerCorrect = true;
nextState.btnColor = {backgroundColor: '#00ff00'};
} else {
nextState.answerCorrect = false;
nextState.btnColor = {backgroundColor: '#ff0000'};
}
return nextState;
},
() => {
setTimeout(() => this.nextQuestion(), 750);
}
);
};
nextQuestion = () => {
this.setState(state => {
const nextIndex = state.activeQuestionIndex + 1;
if (nextIndex >= state.totalCount) {
this.props.navigation.navigate('QuizStatsScreen', {
totalQuizCount: state.totalCount,
correctQuizCount: state.correctCount
});
} else {
return {
activeQuestionIndex: nextIndex,
answered: false,
btnColor: {backgroundColor: '#FFDD7C'}
}
}
});
};
render() {
const questions = this.props.navigation.getParam("questions", []);
const question = questions[this.state.activeQuestionIndex];
return (
<View>
<StatusBar barStyle="light-content" />
<SafeAreaView style={styles.safearea}>
<View>
<Text style={styles.text}>{question.question}</Text>
<ButtonContainer>
{question.answers.map(answer => (
<Button
key={answer.id}
text={answer.text}
onPress={() => this.answer(answer.correct)}
style={this.state.btnColor}
correct={this.state.answerCorrect}
/>
))}
</ButtonContainer>
</View>
</SafeAreaView>
</View>
);
}
}
Button screen:
export const Button = ({ correct, text, style, onPress = () => {} }) => {
return (
<TouchableOpacity onPress={onPress} style={[styles.button, {...style}]}>
<Text style={styles.text}>{text}</Text>
</TouchableOpacity>
);
};
You can use one more variable as selectedId and in your state.
When your click on any answer then check if answer is right or wrong set answerCorrect true if right or false and then store your right and store answer.id to selectedId state.
<Button
....
style={
answer.id === selectedId && answerCorrect ?
styleForCorrectAnswer :
answer.id === selectedId && !answerCorrect ?
styleForWrongAnswer :
styleForDefaultAnswer
}
/>
What we are doing here is first we check if our answer.id match with selected answer id and answer is right. If yes then we are giving styleForCorrectAnswer else we are checking here if answer.id and selectedId match and user's answer is wrong then we are applying styleForWrongAnswer else styleForDefaultAnswer
Do something Like that change the state where you want and it will work
{this.state.correct
?
<TouchableOpacity onPress={onPress} style={{backgroundColor:'green'}}>
<Text style={styles.text}>Correct</Text>
</TouchableOpacity>
:
<TouchableOpacity onPress={onPress} style={{backgroundColor:'red'}}>
<Text style={styles.text}>False</Text>
</TouchableOpacity>
}
This is a simple registration page with username, email, phone num, password and repeat password. The problem lies in repeat password input. This input is supposed to trigger verifyRePassword on text change (just like every other input, and their respective functions). While other inputs have no problem triggering their functions, Repeat Password seems to not trigger its function (verifyRePassword) on text change.
I have tried switching each input with another function, and other inputs seem to trigger the function verifyRePassword just fine.
//FUNCTIONS
verifyPassword = (text) => {
this.setState({password:text})
console.log("verifyPassword Called");
let reg = /\w{6,}/ ;
if(reg.test(text) === false)
{
this.setState({errMsgPassword: "Password must be longer than 6 characters"})
this.setState({password:text})
}
else {
this.setState({errMsgPassword: ""})
this.setState({password:text})
}
}
verifyRePassword = (text) => {
this.setState({repassword: text})
console.log("verifyRePassword Called !!!");
const passStr = toString(this.state.password)
const repassStr = toString(this.state.repassword)
if (passStr === repassStr) {
this.setState({
errMsgRePassword: ""
})
}
else{
this.setState({
errMsgRePassword: "Password must match."
})
}
}
//INPUT
<Text style={stsh.textError}>{this.state.errMsgPhonenum}</Text>
<Text style={stsh.text1}>Password</Text>
<TextInput style={stsh.text2}
onChangeText={this.verifyPassword}
secureTextEntry={true}
/>
<Text style={stsh.textError}>Error {this.state.errMsgPassword}</Text>
<Text style={stsh.text1}>Repeat Password</Text>
<TextInput style={stsh.text2}
onChangeText={this.verifyRePassword.bind(this)}
secureTextEntry={true}
/>
<Text style={stsh.textError}>Error {this.state.errMsgRePassword}</Text>
Since the two are structured similarly, they both should work just fine. Password works, but not Repeat Password.
*edit: some more bits from the code
//CONSTRUCTOR
constructor(){
super()
this.state = {
username: "",
emailadd: "",
phonenum: "",
password: "",
repassword: ""
}
this.verifyUsername = this.verifyUsername.bind(this)
this.verifyEmailadd = this.verifyEmailadd.bind(this)
this.verifyPhonenum = this.verifyPhonenum.bind(this)
this.verifyPassword = this.verifyPassword.bind(this)
}
//COMPLETE FUNCTIONS
verifyUsername = (text) => {
this.setState({username:text})
console.log(text);
let reg = /\w{5,30}/ ;
if(reg.test(text) === false)
{
this.setState({errMsgUsername: "Username must contain 5 to 30 characters"})
this.setState({username:text})
return false;
}
else {
this.setState({errMsgUsername: ""})
this.setState({username:text})
}
}
verifyEmailadd = (text) => {
this.setState({emailadd:text})
console.log(text);
let reg = /^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/ ;
if(reg.test(text) === false)
{
this.setState({errMsgEmailadd: "Enter a valid email address"})
this.setState({emailadd:text})
return false;
}
else {
this.setState({emailadd:text})
this.setState({errMsgEmailadd: ""})
}
}
verifyPhonenum = (text) => {
this.setState({phonenum:text})
console.log(text);
let reg = /\d{10,11}/ ;
if(reg.test(text) === false)
{
this.setState({errMsgPhonenum: "Phone number must be 10 to 11 characters long consisting only of digits"})
this.setState({phonenum:text})
return false;
}
else {
this.setState({errMsgPhonenum: ""})
this.setState({phonenum:text})
}
}
verifyPassword = (text) => {
this.setState({password:text})
console.log("verifyPassword Called");
let reg = /\w{6,}/ ;
if(reg.test(text) === false)
{
this.setState({errMsgPassword: "Password must be longer than 6 characters"})
this.setState({password:text})
}
else {
this.setState({errMsgPassword: ""})
this.setState({password:text})
}
}
verifyRePassword = (text) => {
this.setState({repassword: text})
console.log("verifyRePassword Called !!!");
const passStr = toString(this.state.password)
const repassStr = toString(this.state.repassword)
if (passStr === repassStr) {
this.setState({
errMsgRePassword: ""
})
}
else{
this.setState({
errMsgRePassword: "Password must match."
})
}
}
//COMPLETE RENDER
render() {
return (
<View style={{flex: 1}}>
<SafeAreaView>
<ScrollView style={stsh.container}>
<Text style={{fontSize:40, fontWeight: "bold", borderBottomWidth: 2, color: "#515151", borderColor: "#515151"}}>Sign Up</Text>
<Text style={{color: "#515151",fontSize: 20,fontWeight: "bold",paddingTop: 60}}>Username</Text>
<TextInput style={stsh.text2}
onChangeText={this.verifyUsername}
maxLength={30}
t
/>
<Text style={stsh.textError}>{this.state.errMsgUsername}</Text>
<Text style={stsh.text1}>Email</Text>
<TextInput style={stsh.text2}
value={this.state.email}
onChangeText={this.verifyEmailadd.bind(this)}
/>
<Text style={stsh.textError}>{this.state.errMsgEmailadd}</Text>
<Text style={stsh.text1}>Phone number</Text>
<TextInput style={stsh.text2}
onChangeText={this.verifyPhonenum}
maxLength={11}
/>
<Text style={stsh.textError}>{this.state.errMsgPhonenum}</Text>
<Text style={stsh.text1}>Password</Text>
<TextInput style={stsh.text2}
onChangeText={this.verifyPassword}
secureTextEntry={true}
/>
<Text style={stsh.textError}>Error {this.state.errMsgPassword}</Text>
<Text style={stsh.text1}>Repeat Password</Text>
<TextInput style={stsh.text2}
onChangeText={this.verifyRePassword}
secureTextEntry={true}
/>
<Text style={stsh.textError}>Error {this.state.errMsgRePassword}</Text>
<Text style={{paddingBottom: 50}}></Text>
<Button color='#515151'
onPress={this.submitRegister}
type="outline"
title="Sign Up"
titleStyle={{paddingVertical: 10, fontSize: 30, fontWeight: "bold", color: "#515151"}}
buttonStyle={{borderColor:"#515151", borderWidth: 10, borderRadius: 20}}
/>
<Text style={{paddingBottom: 100}}></Text>
</ScrollView>
</SafeAreaView>
</View>
);
}
}
I've rewrite your verifyRePassword function (I removed the "toString()" as I don't see why it's needed there), try the code below:
verifyRePassword = (text) => {
console.log("verifyRePassword Called !!!", text);
this.setState(()=>{
return {repassword: text};
}, ()=>{
const {password, repassword} = this.state;
this.setState({
errMsgRePassword: password === repassword? "" : "Password must match.",
});
});
}
The issue with comparing the values like you did is that; there is actually a time gap before setState change the value of repassword. Which means; when the line
const repassStr = toString(this.state.repassword)
runs, it actually gets the previous value of "repassword". For this reason, you should use the callback for setState as shown above.
There are two screens addcity and cities. I am sending data from addcity via props and receiving in cities properly and I am trying to iterate data from props via map function but it is not printing data on my device, in fact, I have checked in the console and it is showing data correctly but not in View.
I am using react-naviagtion library with bottomtabnavigator and sending props with navigation props.
app.js
state = {
cities: []
}
addCity = city => {
const cities = this.state.cities;
cities.push(city);
this.setState({ cities })
}
render() {
return (
<Tabs screenProps={{ cities: this.state.cities, addCity: this.addCity }} />
)
}
cities component
render() {
return (
<View>
{
this.props.screenProps.cities.map((city, index) => {
console.log(city);
<Text style={{ color: 'black' }}>{city.city}</Text>
})
}
</View>
)
}
}
addcity component
onSubmit = () => {
if (this.state.city === '' || this.state.country === '') return
const city = {
id: uuidV4(),
city: this.state.city,
country: this.state.country,
locations: []
}
this.props.screenProps.addCity(city);
this.setState({
city: '',
country: ''
}, () => { this.props.navigation.navigate('Cities') });
}
render() {
console.log('props', this.props);
return (
<View style={styles.container}>
<Text style={styles.heading}>Cities App</Text>
<TextInput style={styles.input} placeholder='City' value={this.state.city} onChangeText={val => this.onChangeTextInput('city', val)} />
<TextInput style={styles.input} placeholder='Country' value={this.state.country} onChangeText={val => this.onChangeTextInput('country', val)} />
<TouchableOpacity onPress={this.onSubmit}>
<View style={styles.button}>
<Text style={styles.buttonText}> Add City </Text>
</View>
</TouchableOpacity>
</View>
)
}
}
I am expecting to be city name in Text Component but it showing empty, even though I checked the console and it's consoling my data properly
modify cities component like this:
render() {
return (
<View>
{
this.props.screenProps.cities.map((city, index) => {
console.log(city);
return (
<Text style={{ color: 'black' }}>{city.city}</Text>
)
})
}
</View>
)
}
}
Your map function should construct the text element and return it, you're not returning it.
I'm currently encounter a particular issue with my Edit page code. The problem is as follow: when the user wants to edit their username (on the application), if the user types the (new) username in the textInput field (called 'name')and clicks on the button (Image button of a pencil) , the application is not changing the username. During debugging, the debugger tells me that name is undefined. below follows the code snippet:
edit(name) {
let { user } = this.state;
if (user) {
user.updateProfile({
displayName: name, // here i get the error of 'Undefied'
}).then(() => {
// Update successful.0
}).catch((error) => {
// An error happened.
});
}
}
Below follows the full code of the page:
//constructor
constructor() {
super();
this.state = {
user: {},
fetching: true,
}
this.onAuthStateChanged = this.onAuthStateChanged.bind(this);
}
componentDidMount() {
//Functionality
this.unsubscribeOnAuthChange = firebase.auth().onAuthStateChanged(this.onAuthStateChanged);
}
componentWillUnmount() {
this.unsubscribeOnAuthChange();
}
onAuthStateChanged(user) {
this.setState({ user, fetching: false })
}
edit(name) {
let { user } = this.state;
if (user) {
user.updateProfile({
displayName: name,
}).then(() => {
// Update successful.0
}).catch((error) => {
// An error happened.
});
}
}
ok = () => {
this.props.navigation.navigate('Home');
}
//Styles Account
render() {
let { user, fetching } = this.state;
if(fetching) return null;
return (
<ScrollView>
<View style={styles.container}>
<Text style={styles.text}>Account</Text>
<View style={styles.row}>
<Image source={require('./Picture/userImage1.png')} />
<TouchableOpacity onPress={() => this.edit(user.name)}>
<Image source={require('./Picture/pencil.png')} style={styles.pencil} />
</TouchableOpacity>
</View>
<Text style={styles.text1}>Welcome {user.displayName}</Text>
<TextInput
style={styles.textInput} placeholder='Username'
onChangeText={(name) => this.setState({name})}
underlineColorAndroid='transparent'
autoCapitalize='none'
/>
<TouchableOpacity
style={styles.btn}
onPress={() => this.ok()}>
<Text style={{ fontSize: 17, color: '#fff' }}>Ok</Text>
</TouchableOpacity>
</View>
</ScrollView>
);
}
}
Can anyone give me some advice of why i'm getting an 'undefined" error when the user clicks on the image button?
<TextInput
style={styles.textInput} placeholder='Username'
onChangeText={(name) => this.setState({name})} //<---Here you set value in state call `name`
underlineColorAndroid='transparent'
autoCapitalize='none'
/>
While HERE you are passing the value of object key name
<TouchableOpacity onPress={() => this.edit(user.name)}>
Just simply define name state in this.state and pass the value of state this.state.name in edit function.
I am creating login form. But having some issue about that problem. this is what I have done so far.
loginUser = async(email, password) => {
if(email != '' && password != ''){
try {
let user = await auth.signInWithEmailAndPassword(email,password);
console.log(user);
} catch (error) {
console.log(error);
}
} else {
alert("some error")
}
}
and inside the render:
render(){
return(
<View>
<Text>Email</Text>
<TextInput
onChangeText={(text) => this.setState({email: text})}
value={this.state.email}
/>
<Text>Password</Text>
<TextInput
onChangeText={(text) => this.setState({password: text})}
value={this.state.password}
secureTextEntry={true}
/>
<TouchableHighlight style={{backgroundColor: "green"}}
onPress={() => this.loginUser(this.state.email, this.state.password)}>
<Text>Login!</Text>
</TouchableHighlight>
<TouchableHighlight style={{backgroundColor: "green"}}
onPress={()=> this.loginWithFacebook()}>
<Text>Login with Facebook!</Text>
</TouchableHighlight>
</View>
);
}
}
What am I doing wrong here? Because loginUser function not working properly... Thank you for helping!
Add, this code after the decleration of your class:
state = {
email: '',
password: '',
}
The code is failing because you're trying to get an obj's key which does not exist. You need to creat it first.
I can't see the constructor in the code. Try to add this.
constructor(props){
super(props);
this.state = {
}
}