Reputation: 73
I'm trying to adapt an example seen on an udemy course from a class-based stateful component to a function based component using the useState hook with React 16.7.0-alpha.2
While setter functions for primitive datatypes work fine (for example setUsername), calling a setter for an array variable has no effect/result. At least it doesn't reset the state variable back to an empty array.
On the other hand, setting a new copy of the array from state by using the concat method works as expected.
I'm still new to React hooks and wonder what I've missed?
import React, {useState} from 'react';
import { Grid, Form, Segment, Button, Header, Message, Icon } from 'semantic-ui-react';
import { Link } from 'react-router-dom';
import { registerUser } from './authFunctions';
import { isRegisterFormEmpty } from './validatorFunctions';
const Register = () => {
//defining state properties and setters:
const [username, setUsername] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [passwordConfirmation, setPasswordConfirmation] = useState('');
const [registerErrors, setRegisterErrors] = useState([]);
//defining handlers:
const onUsernameChange = e => setUsername(e.target.value);
const onEmailChange = e => setEmail(e.target.value);
const onPasswordChange = e => setPassword(e.target.value);
const onPasswordConfirmationChange = e => setPasswordConfirmation(e.target.value);
const onFormSubmit = e => {
e.preventDefault(); //prevent a page reload
//set registerErrors to empty array in case that the user clicks on submit again
setRegisterErrors([]); // DOES NOT WORK
setUsername('JDoe'); //works as expected
if( isRegisterFormEmpty(username, email, password, passwordConfirmation) ) {
let error = {message: 'Please fill in all fields'};
setRegisterErrors( registerErrors.concat(error) ); //THIS WORKS FINE, THOUGH...
} else {
//registerUser(username, email, password, passwordConfirmation);
}//if
}//onFormSubmit
const showErrors = () => registerErrors.map( (error, idx) => <p key={idx}>{error.message}</p> );
return (
<Grid textAlign='center' verticalAlign='middle' className='app'>
<Grid.Column style={{ maxWidth: 450 }}>
<Header as='h2' icon color='teal' textAlign='center'>
<Icon name='puzzle piece' color='teal' />
Register to DevChat
</Header>
<Form size='large' onSubmit={onFormSubmit}>
<Segment stacked>
<Form.Input
fluid
type='text'
icon='user'
iconPosition='left'
placeholder='Username'
onChange={onUsernameChange}
value={username}
/>
<Form.Input
fluid
type='email'
icon='mail'
iconPosition='left'
placeholder='Email'
onChange={onEmailChange}
value={email}
/>
<Form.Input
fluid
type='password'
icon='lock'
iconPosition='left'
placeholder='Password'
onChange={onPasswordChange}
value={password}
/>
<Form.Input
fluid
type='password'
icon='lock'
iconPosition='left'
placeholder='Password Confirmation'
onChange={onPasswordConfirmationChange}
value={passwordConfirmation}
/>
<Button
color='teal'
fluid
size='large'
content='Submit'
/>
</Segment>
</Form>
{
registerErrors.length > 0 &&
<Message error>
<h3>Please note</h3>
{showErrors()}
</Message>
}
<Message>
Already a user? <Link to='/login'>Login</Link>
</Message>
</Grid.Column>
</Grid>
)
}
export default Register;
Upvotes: 7
Views: 8726
Reputation: 222493
This is common useState
pitfall.
setRegisterErrors([])
works, there's no chance for it to not work because it's called. It triggers synchronous component update. Since onFormSubmit
doesn't exit after that, setRegisterErrors(registerErrors.concat(error))
is called after that, where registerErrors
is previous state that was defined outside onFormSubmit
.
onFormSubmit
results in 2 register state updates, where second update (concatenated original array) overrides first update (empty array).
A way to fix this is same as with setState
, to use state updater function that provides current state to be updated:
setRegisterErrors(registerErrors => [...registerErrors, error]);
Alternatively, register state updates can be merged:
e.preventDefault();
const registerErrors = [];
setUsername('JDoe');
if( isRegisterFormEmpty(username, email, password, passwordConfirmation) ) {
registerErrors.push({message: 'Please fill in all fields'});
} else {
...
}
setRegisterErrors(registerErrors);
Upvotes: 4