Reputation: 27
During validation inside handleClick function with multiple if statements, only the last one set the errors state to true (errors.sintomas: true) even leaving blank all the inputs (the remaining ones leave it at false, the initial state of errors, thus preventing the inputs to get a red border expcept for the commented last one). So leaving all the inputs blank gives me an errors state of:
{ mascota: false, propietario: false, fecha: false, hora: false, sintomas: true }
Why is this happening? This is all the code:
const Formulario = () => {
const [cita, setCita] = useState({
mascota: '',
propietario: '',
fecha: '',
hora: '',
sintomas: ''
});
const [errors, setErrors] = useState({
mascota: false,
propietario: false,
fecha: false,
hora: false,
sintomas: false
});
const [triggerAnim, setTriggerAnim] = useState(false);
const handleChange = (e) => {
setCita({ ...cita, [e.target.name]: e.target.value });
};
const handleClick = (e) => {
e.preventDefault();
if (cita.mascota.trim().length < 1) {
setErrors({ ...errors, mascota: true });
setTriggerAnim(true);
}
if (cita.propietario.trim().length < 1) {
setErrors({ ...errors, propietario: true });
setTriggerAnim(true);
}
if (cita.fecha.trim().length < 1) {
setErrors({ ...errors, fecha: true });
setTriggerAnim(true);
}
if (cita.hora.trim().length < 1) {
setErrors({ ...errors, hora: true });
setTriggerAnim(true);
}
if (cita.sintomas.trim().length < 1) {
setErrors({ ...errors, sintomas: true });
setTriggerAnim(true);
}
};
return (
<Fragment>
<h2>Crear una cita</h2>
<form>
<label htmlFor="mascota">Nombre de tu mascota:</label>
<input
type="text"
name="mascota"
className={`u-full-width ${errors.mascota ? 'error' : ''}`}
placeholder="Nombre de la mascota"
onChange={handleChange}
value={cita.mascota}
/>
<label htmlFor=" propietario">Nombre del propietario:</label>
<input
type="text"
name="propietario"
className={`u-full-width ${errors.propietario ? 'error' : ''}`}
placeholder="Nombre del propietario"
onChange={handleChange}
value={cita.propietario}
/>
<label htmlFor="fecha">Fecha:</label>
<input
type="date"
name="fecha"
className={`u-full-width ${errors.fecha ? 'error' : ''}`}
onChange={handleChange}
value={cita.fecha}
/>
<label htmlFor="hora">Hora:</label>
<input
type="time"
name="hora"
className={`u-full-width ${errors.hora ? 'error' : ''}`}
onChange={handleChange}
value={cita.hora}
/>
<label htmlFor="sintomas">Sintomas</label>
<input
type="text"
name="sintomas"
className={`u-full-width ${errors.sintomas ? 'error' : ''}`}
placeholder="Síntomas de la mascota:"
onChange={handleChange}
value={cita.sintomas}
/>
<button className="button-primary u-full-width" onClick={handleClick}>
Crear Cita
</button>
</form>
</Fragment>
);
};
export default Formulario;
Don't understand why only the last if statement is working. If I use setErrors(prevState => {return {...prevState, mascota: true}) (the callback with the previous state) then suddenly all works OK. Why using object spread operator to update state does not work?
Upvotes: 1
Views: 883
Reputation: 371029
If you click once, the whole handleClick
function will also run through its code fully once. Inside that one handleClick
, the errors
object is constant and is never mutated; it refers to what errors
contained during the render before the click.
Say the initial errors
object's values were all false
the render before click. Then, if the click handler detects a mascota
error:
setErrors({ ...errors, mascota: true });
will result in an object with all false
properties, except for mascota
, whose property is true.
Next, if there's a propietario
error as well, the following will run:
setErrors({ ...errors, propietario: true });
which sets the state to the initial errors object (with all false
values), combined with a single true property in propietario
. The mascota
property in the new state will be false
, because the errors
object that was referenced contained mascota: false
, and it was combined with propietario: true
. And so on.
You need the callback form so that the new state can be calculated properly even if it was just updated by a prior synchronous call of setErrors
, otherwise those prior synchronous calls will not factor into the new state.
Rather than all of this manual validation, you might also consider adding required
attributes to the inputs.
Upvotes: 1