Reputation: 141
i'm doing this pretty straight forward ReactJS login screen and there's a bug (at least i think so) in the setValue state. Basically, i set the default values for those values (as seen below)
const [values, setValues] = React.useState<State>({
email: '',
password: '',
showPassword: false,
stayLogged: false,
openSnackBar: false,
incorrectEmail: false,
incorrectPassword: false,
emailErrorMessage: '',
passwordErrorMessage: ''
});
I can change the other fields (including the openSnackBar), but not the incorrectEmail and incorrectPasswords fields. As you can see, in the snippet below i verify if the text matches the regex of a valid email address, if not, it should update the values with a new error message and true for incorrectEmail, but it doesn't. The same goes for incorrectPassword.
const verifyEmail = (email: React.ChangeEvent<HTMLInputElement>) => {
let emailVerificationRegex = '^\\w+([-+.\']\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$';
if ((!new RegExp(emailVerificationRegex).test(email.target.value)) && email.target.value !== '') {
setValues({...values, incorrectEmail: true})
setValues({...values, emailErrorMessage: 'The email you entered is invalid'})
log('If email invalido: \nValor incorrectEmail = ' + values.incorrectEmail + '\nvalor emailErrorMessage ' + values.emailErrorMessage)
} else {
setValues({...values, incorrectEmail: false})
setValues({...values, emailErrorMessage: ''})
setValues({...values, email: email.target.value})
console.log('If email valido: \nValor incorrectEmail = ' + values.incorrectEmail + '\nvalor emailErrorMessage ' + values.emailErrorMessage + '\nemail' + values.email)
}
}
What is causing this behaviour? How can i fix it? full snippet below
import React from 'react';
import {createStyles, makeStyles, Theme} from '@material-ui/core/styles';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import {
Button,
FormControlLabel,
Grid,
IconButton,
InputAdornment,
Link,
Snackbar,
Switch,
TextField
} from "@material-ui/core";
import {Close, Facebook, Visibility, VisibilityOff} from "@material-ui/icons";
import {log} from "util";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
maxWidth: 345,
},
buttonSpacing: {
padding: theme.spacing(2)
},
marginSpacing: {
margin: theme.spacing(1)
},
loginCardSpacing: {
margin: theme.spacing(5)
},
forgotYourPassword: {
paddingRight: 10
},
}),
);
interface State {
email: string;
password: string;
showPassword: boolean;
stayLogged: boolean;
openSnackBar: boolean;
incorrectEmail: boolean;
incorrectPassword: boolean;
emailErrorMessage: string;
passwordErrorMessage: string;
}
export default function LoginCard() {
const classes = useStyles();
const [values, setValues] = React.useState<State>({
email: '',
password: '',
showPassword: false,
stayLogged: false,
openSnackBar: false,
incorrectEmail: false,
incorrectPassword: false,
emailErrorMessage: '',
passwordErrorMessage: ''
});
const showHidePassword = () => {
setValues({...values, showPassword: !values.showPassword})
}
const stayConnected = () => {
setValues({...values, stayLogged: !values.stayLogged})
}
const handleCloseSnackBar = () => {
setValues({...values, openSnackBar: !values.openSnackBar})
}
const verifyEmail = (email: React.ChangeEvent<HTMLInputElement>) => {
let emailVerificationRegex = '^\\w+([-+.\']\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$';
if ((!new RegExp(emailVerificationRegex).test(email.target.value)) && email.target.value !== '') {
setValues({...values, incorrectEmail: true})
setValues({...values, emailErrorMessage: 'The email you entered is invalid'})
log('If email invalido: \nValor incorrectEmail = ' + values.incorrectEmail + '\nvalor emailErrorMessage ' + values.emailErrorMessage)
} else {
setValues({...values, incorrectEmail: false})
setValues({...values, emailErrorMessage: ''})
setValues({...values, email: email.target.value})
console.log('If email valido: \nValor incorrectEmail = ' + values.incorrectEmail + '\nvalor emailErrorMessage ' + values.emailErrorMessage + '\nemail' + values.email)
}
}
const verifyPassword = (password: React.ChangeEvent<HTMLInputElement>) => {
if ((password.target.value.length < 5 || password.target.value.length > 24 || !password) && !(password.target.value === '')) {
setValues({...values, incorrectPassword: true})
setValues({...values, passwordErrorMessage: 'The password you entered is invalid'})
console.log('If senha invalida: ' + values.incorrectPassword + ' ' + values.passwordErrorMessage)
} else {
setValues({...values, incorrectPassword: false})
setValues({...values, passwordErrorMessage: ''})
setValues({...values, password: password.target.value})
console.log('else senha válida: ' + values.incorrectPassword + ' ' + values.passwordErrorMessage)
}
}
function loginToApp(): any {
if (!values.incorrectPassword || !values.incorrectEmail) {
setValues({...values, openSnackBar: !values.openSnackBar})
}
}
return (
<div id="container" className={classes.loginCardSpacing}>
<Grid
container
direction="row"
justify="center"
alignItems="center">
<Grid>
<Card className={classes.root}>
<CardContent>
<form noValidate autoComplete="off">
<TextField id="loginField"
label="Email"
variant="filled"
fullWidth
margin="normal"
error={values.incorrectEmail}
onChange={verifyEmail}
helperText={values.emailErrorMessage}/>
<TextField id="passwordField" label="Password"
type={values.showPassword ? 'text' : 'password'}
onChange={verifyPassword}
variant="filled"
fullWidth
margin="normal"
error={values.incorrectPassword}
helperText={values.passwordErrorMessage}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={showHidePassword}
>
{values.showPassword ? <Visibility/> : <VisibilityOff/>}
</IconButton>
</InputAdornment>
),
}}/>
<div id="buttonGroup">
<Grid
className={classes.marginSpacing}
container
direction="row"
justify="space-around"
alignItems="flex-start"
>
<FormControlLabel
control={<Switch size="small" checked={values.stayLogged}
onChange={stayConnected} name="stayLoggedSwitch"/>}
label="Stay Logged"
/>
<Link className={classes.forgotYourPassword}>
Forgot your password?
</Link>
</Grid>
<Grid container
direction="column"
justify="center"
alignItems="center"
className={classes.buttonSpacing}>
<Grid>
<Button variant="contained" onClick={loginToApp}>
Login
</Button>
</Grid>
<Grid className={classes.marginSpacing}>
<Button
variant="contained"
color="primary"
startIcon={<Facebook/>}
>
Login with Facebook
</Button>
</Grid>
</Grid>
</div>
</form>
</CardContent>
</Card>
</Grid>
</Grid>
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center',
}}
open={values.openSnackBar}
autoHideDuration={6000}
onClose={handleCloseSnackBar}
message={values.passwordErrorMessage}
action={
<React.Fragment>
<IconButton size="small" aria-label="close" color="inherit" onClick={handleCloseSnackBar}>
<Close fontSize="small"/>
</IconButton>
</React.Fragment>
}
/>
</div>
);
}
Upvotes: 0
Views: 502
Reputation: 1426
What I usually to in these situations and that I can recommend to you, is to create a clone of the whole state object, modify and update state afterwards, just like this example:
const verifyPassword = (password: React.ChangeEvent<HTMLInputElement>) => {
if (condition) {
let clone = {...values};
clone.incorrectPassword = true;
setValues(clone);
}
}
OR simply call setValues once including the corresponding changes:
setValues({
...values ,
incorrectEmail: true ,
emailErrorMessage: 'The email you entered is invalid'
})
Upvotes: 1
Reputation: 452
this is common mistake in react, and you can not change values
state using values
.
you must replace
setValues({...values, incorrectEmail: false})
with below code :
setValues(prevValues=>({...prevValues, incorrectEmail: false}))
and do this in your other setValues
Upvotes: 0
Reputation: 2363
You are calling multiple setState one after another . As a result the state is getting messed up. One possible solution is to use async await function. But i think for your issue simply updating state in one call is enough. Something like this.
setValues({
...values ,
incorrectEmail: true ,
emailErrorMessage: 'The email you entered is invalid'
})
Upvotes: 1