Tambien podriamos agregar un setError(false), cuando se da click en el botón comprobar…
<button
onClick={() => {
setLoading(true);
setError(false);
}} >
Check
</button>
Introducción
Dime cómo manejas el estado y te diré...
Estado y ciclo de vida con React.Component
Nuevo proyecto: códigos de seguridad
Estados simples: React.Component vs. useState
Efectos con useEffect
Métodos del ciclo de vida en React.Component
Estados independientes y compuestos
Estados independientes con useState
¿Dónde actualizar el estado?
Estados compuestos con React.Component
Estados compuestos con useState
Código imperativo y declarativo en React
Estados imperativos con useState
Estados semideclarativos con useState
¿Qué es un reducer?
3 formas de crear un reducer
Estados declarativos con useReducer
Action creators y actionTypes
Manejo del estado en TODO Machine
¿Qué son los estados derivados?
Migración de useState a useReducer en useLocalStorage
Imponer tu visión vs. trabajar en equipo
Próximos pasos
¿Quieres más cursos de React.js?
Aún no tienes acceso a esta clase
Crea una cuenta y continúa viendo este curso
Aportes 44
Preguntas 0
Tambien podriamos agregar un setError(false), cuando se da click en el botón comprobar…
<button
onClick={() => {
setLoading(true);
setError(false);
}} >
Check
</button>
Reto de la clase
manteniendo el mismo código, solo agregando un else con setError(false)
useEffect(
() => {
if (loading) {
setTimeout(() => {
if (value !== SECURITY_CODE) {
setError(true)
}else{
setError(false)
}
setLoading(false)
}, 1000)
}
}, [ loading ] )
También se le pudiera poner un setTimeout al setError para quitarlo. La otra forma que se me ocurre es que en el evento onChange al ejecutarse, poner setError(false), así escribes en el input y nunca tendrás un error mostrándose 😃
Yo simplemente agregaria un setError(false) al inicio del efecto
if (loading) {
setError(false);
setTimeout(() => {
console.log("Haciendo la validación");
if (value === SEGURITY_CODE) {
setLoading(false);
} else {
setError(true);
setLoading(false);
}
console.log("terminando la validación");
}, 1000)
}
Hice una función llamada handleChange() en donde el estado de error lo cambio a falso, de esta forma en cuanto el usuario modifique el texto del input, el mensaje de error desaparece 😃
const handleChange = (event) => {
setValue(event.target.value);
setError(false);
}
<input
type="text"
placeholder="Código de seguridad"
value={value}
onChange={(event) => handleChange(event)}
/>
Mi manera de solucionarlo es que cuado loading es true, llamamos a setError(false) para que desaparezca el mensaje y solo se vea el de “Cargando…” desués si el valor es igual al codgo de seguridad tambien ponemos setError(false) para que no nos estorbe.
React.useEffect(() => {
console.log("Empezando...")
if (!!loading) {
setError(false);
setTimeout(() => {
console.log("Haciendo la validacion...")
if(value === SECURITY_CODE) {
setLoading(false);
setError(false);
} else {
setLoading(false);
setError(true);
}
console.log("Terminando la validacion...")
}, 2000)
}
console.log("Acabando...")
}, [loading])
// This function is called when the form is submitted
const onValidation = (e) => {
e.preventDefault();
setMessage('');
setLoading(true);
};
{message && (
<p className={error ? 'text-red-400' : 'text-green-400'}>{message}</p>
)}
Realizaríamos los siguientes cambios:
<button
onClick={handleValidar} >
Comprobar
</button>
Luego crearíamos la siguiente función.
const handleValidar = () => {
setError(false)
setLoading(true)
}
Y finalmente quedaría el useEffect asi:
useEffect(() => {
if (loading) {
setTimeout(() => {
if (value === SECURITY_CODE) {
setError(false)
} else {
setError(true)
}
setLoading(false)
}, 3000)
}
}, [loading])
useEffect(()=>{
if(loading )
{
console.log("change state loading")
setTimeout(()=>{
if(value !== SECURITY_CODE)
{
setError(true);
}
if(value === SECURITY_CODE)
{
if(error)
{
setError(false);
}
}
SetLoading(false);
},2000)
}
},[loading])
Utilicé otro useEffect, para que inmediatamente el usuario cambie el valor del input se elimine el mensaje de error:
useEffect(() => {
setError(false)
}, [value])
if (!!loading) {
setError(false);
setTimeout(() => {
if (value !== SECURITY_CODE) {
setError(true);
}
setLoading(false);
}, 3000);
}
Le agregué el estado del error a useEffect y luego retorné un array seteando los valores, es la manera que se me ocurrió
<button onClick={() => [setLoading(true), setError(false)]}>Comprobar</button>
A mi me parece que lo mejor es poner el setError en el boton de comprobar
React.useEffect(() => {
if (loading) {
setTimeout(() => {
if (value !== SECURITY_CODE) {
setError(prev => !prev);
} else if ((value === SECURITY_CODE) && error) {
setError(prev => !prev);
setLoading(false);
}
setLoading(false);
}, 1000)
}
}, [loading, error]);
Yo simplemente agregue un setError(false) cuando el value del elemento sea verdadero y ya.
Mi solucion
React.useEffect(() => {
if (!!loading) {
setError(false);
setTimeout(() => {
if(value === SECURITY_CODE) {
setLoading(false);
} else {
setLoading(false);
setError(true);
}
}, 3000);
}
}, [loading]);
Antes de la validación:
error ? setError(false) : null;
if(value !== SECURITY_CODE)
Buenas, mi solucion fue agregar dos cosas, junto a la primera validacion de setLoading false, tambien coloque setError false, y en el return, se coloco una segunda validacion para que solo mostrara el parrafo de error si error era true y si loading era false:
if(loading){
setTimeout(()=>{
console.log('validating')
if(value=== SECURITY_CODE){
setLoading(false);
setError(false);
}else{
setError(true);
setLoading(false);
}
console.log('ok')
},3000)
}
console.log('finishing')
},[loading])
return(
<div>
<h2>Delete {name}</h2>
<p>Write the access code</p>
{(error && !loading) && <p>
Error, incorrect code
</p>}
Mi solución fue crear una función onChangeInput y quitar el alert de error dentro de esta:
const onChangeInput = (e) => {
setValue(e.target.value)
setError(false)
}
<input
type='text'
placeholder="Código de seguridad"
className="border border-gray-200 rounded-md px-1 py-1 mr-2 outline-none
focus:ring-2 focus:ring-blue-400"
value={value}
onChange={(e) => onChangeInput(e)}
/>
Me salio así:
if (!!loading) {
setTimeout(() => {
console.log("Haciendo la validación");
if (value !== SECURITY_CODE) {
setLoading(true);
setError(true);
} else {
setError(false)
}
setLoading(false)
console.log("Términando la validación");
}, 3000);
}
Yo solamente agregaría el setError(false) en el evento onClick del botón.
React.useEffect(() => {
console.log("Empezando el efecto");
if (!!loading) {
setTimeout(() => {
console.log("Haciendo la validacion");
if (value !== SECURITY_CODE) {
setError(true);
} else {
setError(false);
}
setLoading(false);
console.log("Terminando la validacion");
}, 3000)
}
console.log("Finalizando el efecto");
}, [loading]);
return (
<div>
<h2>Eliminar {name}</h2>
<p> Por favor, escribe el código de seguridad</p>
{error && !loading && (
<p>El código es incorrecto</p>
)}
{loading && (
<p>Cargando ...</p>
)}
<input placeholder="Código de seguridad"
value={value}
onChange={(event) => { setValue(event.target.value) }}
/>
<button onClick={() => setLoading(!loading)}> Comprobar</button>
</div >
)
La forma que yo creo que es más óptima es usando un solo estado que maneje el status de la llamada a la API:
const [status, setStatus] = useState('idle') // idle, pending, resolved, rejected
Artículo de Kent C. Dodds que habla sobre esto:
https://kentcdodds.com/blog/stop-using-isloading-booleans
Código completo:
export default function UseState() {
const [status, setStatus] = useState('idle') // idle, pending, resolved, rejected
const [value, setValue] = useState('');
useEffect(() => {
if (status === 'pending') {
setTimeout(() => {
if (value === SECURITY_CODE) {
setStatus('resolved');
} else {
setStatus('rejected');
}
}, 2000)
};
}, [status, value]);
return (
<div>
<h2>Eliminar UseState</h2>
<p>Por favor, bla bla bla</p>
{status === 'rejected' && (
<p style={{ color: 'salmon' }}>Error: código incorrecto</p>
)}
{status === 'pending' && (
<p style={{ color: 'lightblue' }}>Cargando...</p>
)}
<input
type="text"
placeholder='código de seguridad'
value={value}
onChange={(e) => setValue(e.target.value)}
/>
<button
onClick={() => setStatus('pending')}
>
Comprobar
</button>
</div>
);
}
Colocando la validación dentro del if al hacer click en el boton con el codigo incorrecto y luego al correcto, permanece los 3 segudos de espera el mensaje de error y luego se elimina.
if (!!loading) {
setTimeout(() => {
console.log("Ejecutando validacion")
if(value === SECURITY_CODE) {
setLoading(false);
setError(false);
} else {
setError(true);
setLoading(false);
}
console.log("Terminando validacion")
}, 3000)
}
Pero lo ideal es que al momento de hacer click nuevamente se elimine el error y quede en loading, lo que hice fue colocar dentro del button que el error sea (false), asi cada vez que lo utilizamos se elimina el mensaje.
<button
onClick={() => {
setError(false);
setLoading(!loading);
}}
>Comprobar</button>
if (loading) {
setTimeout(() => {
if (value !== SECURITY_CODE) {
setLoading(false);
setError(true);
return;
}
setLoading(false);
setError(false);
}, 2000);
La forma mas simple que se me ocurrio fue poner el setError(false), antes del timeout, para asegurarnos que sin importar si esta correcta o incorrecta la contrasenia, se desmonte el componente de error cada vez que se vaya a volver a comprobar.
React.useEffect(()=> {
console.log('Empezando el efecto');
if(loader){
setError(false);
setTimeout(()=>{
console.log('Haciendo la validacion');
if(value === SECURITY_CODE){
} else{
setError(true);
}
setLoader(false);
console.log('Terminando la validacion');
}, 2000);
}
console.log('Terminando el efecto');
}, [loader])
Yo lo puse al principio de cada comprobación para que inicie sin errores y separado
de toda la lógica de loading para que esta parte del código pueda cambiar inclusive desaparecer
sin problemas pero el error siempre que resuelto.
<button onClick={() => {
setError(false)
setLoading(true)
}}>
Comprobar
</button>
if (value !== SECURITY_CODE) {
setError(true);
} else if (!!error) {
setError(false);
}
setLoading(false);
Así es como yo lo solucione
const SECURITY_CODE = "paradigma";
function handleInput(e, setValue) {
setValue(e.target.value);
}
function handleClick(setLoading, value, setValidation) {
if (value === SECURITY_CODE) {
setValidation("Continue");
} else {
setValidation("Error");
}
setLoading(true);
}
function UseState({ name }) {
const [value, setValue] = React.useState("");
const [validation, setValidation] = React.useState("");
const [loading, setLoading] = React.useState(false);
React.useEffect(() => {
if (loading) {
setTimeout(() => {
setLoading(false);
}, 1000);
}
}, [loading]);
return (
<div>
<h2>Eliminate {name}</h2>
<p>Please, type security code</p>
{validation && !loading && <p>{validation}</p>}
{loading && <p>Loading...</p>}
<input
onChange={(e) => handleInput(e, setValue)}
type="text"
placeholder="Secutiry code"
/>
<button onClick={() => handleClick(setLoading, value, setValidation)}>
Check
</button>
</div>
);
}
Retro de la clase
Lo primero que se me ocurrio era meterle la condicion directamente al setError para actualizar el estado.
useEffect(() => {
console.log("Empezando el efecto");
if (loading) {
setTimeout(() => {
console.log("Haciendo la validacion");
setError(value !== SECURITY_CODE);
setLoading(false);
console.log("Terminando la validacion");
}, 2000);
}
console.log("Terminando el efecto");
}, [loading]);
React.useEffect(() => {
console.log('Principio efecto');
if(!!loading){
setError(false);
setTimeout(() => {
console.log('Haciendo validacion');
if(value !== SECURITY_CODE){
setError(true);
}
setLoading(false);
console.log('Final validacion');
}, 2000)
}
console.log('final efecto');
},[loading])
De esta forma solo se aplicara el cambio de estado solo cuando se le de al boton que cambia el estado de carga.
Esta fue la primera forma que use. Aunque como vi en los comentarios una opción que usar menos recursos es en el onClick o el inicio de useEffect
onChange={(event) => {
setError(false);
setValue(event.target.value);
}}
Simplemente valide si el error era true y quitarlo antes de validar.
useEffect(() => {
console.log('Empezando el estado')
if(!!loading) {
if(error) setError(false)
setTimeout(() => {
console.log('Haciendo la validación')
setLoading(false)
if(value !== SECURITY_CODE) {
setError(true)
}
console.log('Terminando la validación')
}, 3000)
}
console.log('Terminando el efecto')
}, [loading])
import { useState, useEffect } from "react";
const SECURITY_CODE = "parapeto";
export const UseState = (props) => {
const [value, setvalue] = useState("");
const [error, seterror] = useState(false);
const [loading, setloading] = useState(false);
useEffect(() => {
if (loading)
setTimeout(() => {
if (value !== SECURITY_CODE) seterror(true)
setloading(false);
}, 1000);
}, [loading]);
const handleInput = (e) => {
setvalue(e.target.value);
};
const handleClick = () => {
seterror(false)
setloading(true);
};
return (
<>
<h2> Eliminar {props.name} </h2>
<p> Por favor escribe el código de seguridad </p>
{error && <p> ❌ Error: código incorrecto </p>}
{loading && <p> ⏳ Cargando </p>}
<input
placeholder="código de seguridad"
onChange={handleInput}
value={value}
/>
<button onClick={handleClick}> Comprobar </button>
</>
);
};
Con algunas modificaciones UseState.js estaría de la siguiente manera:
import React from 'react'
const SECURITY_CODE = 'paradigma';
function UseState({ name }) {
const [value, setValue] = React.useState('');
const [error, setError] = React.useState(false);
const [loading, setLoading] = React.useState(false);
const onClick = (event) => {
setLoading(true)
}
const onChange = (event) => {
setValue(event.target.value)
}
const commentEnterSubmit = (event) => {
if (event.key === "Enter" && event.shiftKey === false) {
return onClick(event);
}
}
React.useEffect(() => {
console.log("Starting the effect");
if (!!loading) {
setTimeout(() => {
console.log("Doing the validation");
if(value === SECURITY_CODE){
setLoading(false);
setError(false);
}
else{
setLoading(false);
setError(true);
}
console.log("Finishing the validation");
}, 2000);
}
console.log("Finishing the effect");
}, [loading]);
return (
<div>
<h2>Delete {name}</h2>
<p>Please enter the security code</p>
{error && (
<p>Error: Security code is incorrect</p>
)}
{loading && (
<p>Loading...</p>
)}
<input
placeholder="Security Code"
value={value}
onChange={onChange}
onKeyPress={commentEnterSubmit}
/>
<button
onClick={onClick}
>Check</button>
</div>
);
}
export { UseState }
No se que tan buena sea mi solución pero ahi esta XD, a la siguiente clase!
React.useEffect(() => {
if (loading) {
console.log("Empezando el efecto");
setTimeout(() => {
console.log("Empezando la validación");
if (value !== SECURITY_CODE) {
setError(true);
}
setLoading(false)
console.log("Terminando la validación");
}, 3000);
setTimeout(() => {
setError(false);
}, 5000)
console.log("Terminando el efecto");
}
}, [loading]);
Reto de la clase
Yo lo soluione cambiando cambiando el estado de Error a false cada vez que modifiquemos nuestro input
<input
placeholder="codigo de seguridad"
value={value}
onChange={event=>{
setError(false);
setValue(event.target.value);
}
}
/>
< React.useEffect( () => {
console.log("empezando el efecto")
if(!!loading){
setTimeout( () => {
console.log("haciendo la validacion")
if(value === SECURITY_CODE){
setError(false);
}else {
setError(true);
}
setLoading(false)
console.log("Terminando la validaciin la validacion")
}, 3000 )
}
import React from "react";
const SECURITY_CODE = "paradigma";
function UseState({ name }) {
const [value, setValue] = React.useState('');
const [error, setError] = React.useState(false);
const [loading, setLoading] = React.useState(false);
console.log(value);
React.useEffect(() => {
console.log("Starting the effect");
if(!!loading) {
setTimeout(() => {
console.log("Doing the validation");
if(value === SECURITY_CODE) {
setLoading(false);
setError(false);
} else {
setError(true);
setLoading(false);
}
console.log("Finishing the validation");
}, 1000);
}
console.log("Finishing the effect");
}, [loading]);
return (
<div>
<h2>Delete {name}</h2>
<p>Please enter the security code</p>
{(error && !loading) && (
<p>Error: Security code is incorrect</p>
)}
{loading && (
<p>Loading...</p>
)}
<input
placeholder="Security Code"
value={value}
onChange={(event) => {
//setError(false);
setValue(event.target.value);
}}
/>
<button
onClick={() => {
setLoading(true);
//setError(false);
}}
>Check</button>
</div>
);
}
export { UseState };
Solución del reto:
Para conseguir que el mensaje de error desaparezca mientras carga y vuelva aparecer si hay error, entonces escribi este simple codigo:
React.useEffect(()=>{
console.log('Empezando el efecto');
if(loading){
setError(false);
setTimeout(()=>{
console.log("Haciendo la validación xd");
if(value === SECURITY_CODE){
setLoading(false);
}else{
setError(true);
setLoading(false);
}
console.log("Terminando la validación");
},1500)
}
console.log('Terminando el efecto');
},[loading]);
Un hechizo simple, pero efectivo.
Tengo dos soluciones, la primera es la siguiente.
useEffect(()=>{
console.log('Empezando el efecto')
if ( !!loading ) {
setTimeout(()=>{
console.log('Haciendo la validación');
if ( value !== SECURITY_CODE ) {
setError(true);
} else {
setError(false);
}
setLoading(false);
console.log('Terminando la validación');
},3000);
}
console.log('Terminando el efecto')
}, [loading]);
Pero hay un problema, gráficamente se seguira viendo el Error mientras se realiza el useEffect, por lo cual hice esta segunda opción.
useEffect(()=>{
console.log('Empezando el efecto')
if ( !!loading ) {
setError(false);
setTimeout(()=>{
console.log('Haciendo la validación');
if ( value !== SECURITY_CODE ) {
setError(true);
}
setLoading(false);
console.log('Terminando la validación');
},3000);
}
console.log('Terminando el efecto')
}, [loading]);
Hola, a mi se me ocurría hacer lo siguiente.
Al principio de la validación de effect hacer set de error a false:
useEffect(() => {
if(loading){
setError(false)
setTimeout(() => {
console.log("haciendo la validación")
if(value !== SECURITY_CODE){
setError(true);
}
setLoading(false);
console.log("terminando la validación")
}, 1500);
}
},[loading])
o haciendole una validación al mismo error
useEffect(() => {
if(loading){
if(error){
setError(false)
}
setTimeout(() => {
console.log("haciendo la validación")
if(value !== SECURITY_CODE){
setError(true);
}
setLoading(false);
console.log("terminando la validación")
}, 1500);
}
},[loading])
Una opción podría ser en el botón:
<button
onClick={() => {
setError(false)
setLoading(!loading)
}
}
>Comprobar</button>
Otra, antes de setTimeout:
if (!!loading) {
setError(false);
setTimeout(() => {
console.log("Haciendo la validacion")
if(value !== SECURITY_CODE) {
setError(true);
}
setLoading(false);
console.log("Terminando la Validacion")
}, 3000)
}
y otra que se me ocurrió es en el input, teniendo en cuenta que se borra apenas tecleen en el, pero si no borran el anterior texto no se quitaría el mansaje de error:
<input
placeholder="Codigo de seguridad"
value={value}
onChange={(event) => {
setValue(event.target.value);
setError(false);
}}
/>
¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.