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?
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
Juan David Castro Gallego
Aportes 115
Preguntas 3
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)
}
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])
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)}
/>
// 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>
)}
Según yo la forma un poco mas eficiente y con mejor experiencia de usuario es quitar el error al momento que el usuario cambia el input nuevamente de la siguiente manera
comprobando primero si el error es true, de esta forma evitamos cambiar el estado a false cada que el usuario presione una tecla
<input placeholder="Codigo de seguridad" value={value}
onChange={ev => {
if (error) {
setError(false)
}
setValue(ev.target.value)
}}/>
Lo colocaría después de la validación de la variable loading:
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")
}, 3000)
}
}, [loading])
este es mi solución al problema que cuando le den click al boton el setError pase a false
Agregando el setError antes del setTimeout
React.useEffect(()=>{
console.log("Empezando el efecto")
if(!!loading){
setError(false);
setTimeout(()=>{
setLoading(false);
console.log("Haciendo la validacion")
if (value!== SECURITY_CODE) {
setError(true);
}
console.log("Terminando la validacion")
},3000)
}
console.log("Terminando el efecto")
},[loading])
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])
Laziest quick way:
{error && !loading && <Error />}
Lo primero que se me ocurrio fue hacer esto:
React.useEffect(() => {
console.log("Empesando el efecto")
if (!!loading) {
setTimeout(() => {
console.log("Haciendo la validación")
value !== SECURITY_CODE ? setError(true) : setError(false)
setLoading(false)
console.log("Terminando la validación")
}, 3000)
}
console.log("Terminando el efecto")
}, [loading])
Pero viendo en los comentarios tiene sentido que cuando volvemos a validar limipiemos el error mientras carga y luego dependiendo si vuelve a existir lo mostremos o no
React.useEffect(() => {
console.log("Empesando 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])
Lo que utlicé fue un onClick en el input.
<input
onClick={()=> setError(false)}
placeholder="Código de seguridad"
value={value}
onChange={(event)=>{
setValue(event.target.value)
}}
En mi boton
La función que maneja el click
El **useEffect **que reacciona cuando cambia el loading
Mi solucion:
if(!!loading){
setError(false);
setTimeout(() => {
console.log("iniciando validacion")
if(value === SECURITY_CODE){
setLoading(false);
}else{
setError(true);
setLoading(false);
}
console.log("terminando la validacion")
}, 3000)
};
Yo lo resolví creando un state [message, setMesagge] con un valor inicial “Por favor introduce el codigo de seguridad”
.
Ese estado es lo que se renderiza como <p> </p> en mi interfaz arriba del input.
.
Entonces cuando el codigo es incorrecto tengo que actualizar 3 estados. El de loading a false, el de error a true y el de message con el mensaje que quiero mostrar.
.
Esto funciona pero son 3 estados que se actualizan, por lo que puede tornarse complejo. El componente se re renderiza 2 veces (1 cuando pulso el boton que actualiza el loading y otra cuando entra al effect y ejecuta las 3 ejecuciones del estado)
En mi caso, decidí crear una función para validar el valor del input independiente del useEffect
:
import { useState, useEffect } from "react";
interface UseStateProps {
name: string;
}
const SECURITY_CODE = "paradigma";
const UseState: React.FC<UseStateProps> = ({ name }) => {
const [value, setValue] = useState<string>("");
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<boolean>(false);
const [validated, setValidated] = useState<boolean>(false);
useEffect(() => {
if (loading) {
setTimeout(() => {
setLoading(false);
}, 2000);
}
}, [loading]);
const validateSecurityCode = (value: string): void => {
setError(false);
const isValid = value === SECURITY_CODE;
if (isValid) {
setValidated(true);
} else {
setError(true);
}
};
return (
<div>
<h2>Eliminar {name}</h2>
<p>Por favor, escriba el código de seguridad.</p>
{validated && <p>El código de seguridad es correcto. ✅</p>}
{error && <p>El código de seguridad es incorrecto. ❌</p>}
{loading && <p>Cargando...</p>}
<input
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Código de seguridad"
type="text"
/>
<button onClick={() => validateSecurityCode(value)}>Comprobar</button>
</div>
);
};
export { UseState };
Mi versión simplificada del useEffect
React.useEffect(() => {
if (!loading) return;
setError(false);
setTimeout(() => {
setLoading(false)
if (value !== SECURITY_CODE) return setError(true);
setError(false)
}, 1000);
}, [loading]);
lo hice asi
Yo puse el setErro(false) en el input:
<input
placeholder="Security Code"
value={value}
onChange={event=>{
setValue(event.target.value)
setError(false )
}}
Yo quise usar un ternario en el código en vez de los if, else/
React.useEffect(()=>{
const wrongCode = () =>{
setLoading(false);
setError(true);
}
if(!!loading){
setTimeout(()=>{
value === SECURITY_CODE ? setLoading(false) : wrongCode();
}, 3000)
}
}, [loading])
Mi solución fue crear una función llamada validateError
const validateError = (event) => {
setValue(event.target.value);
setError(false);
};
Finalmente esta función la uso en el onClick
<input type="text" placeholder="Código de seguridad" value={value} onChange={validateError}/>
Se podria utilizar setError(false)
, si el error es true
, en onChange
del input
La forma más óptima que pude encontrar:
React.useEffect(() => {
if (loading) {
setError(false);
setTimeout(() => {
setLoading(false);
if (value !== SECURITY_CODE) {
setError(true);
return;
}
setError(false);
}, 3000);
}
}, [loading]);
Para que desaparezca el títula antes de empezar a cargar lo llamo antes de que entre al setTimeout !!!
React.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);
}
Mi aporte sería este ->
React.useEffect(() => {
if (!!loading) {
setError(false);
setTimeout(() => {
setError(value !== SECURITY_CODE);
setLoading(false);
}, 3000);
}
}, [loading]);
He estado viendo reels de midudev en IG y mencionaba que los if() a veces suman mucho código así que es mejor retornar la validación, de esta forma te ahorras mucho código, así mi solución más rápida y sencilla es solo agregando una linea de código:
useEffect(()=>{
if(loading){
setTimeout(()=>{
setError(value !== SECURITY_CODE)
setLoading(false);
}, 3000);
}
}, [loading])
Con esta solución te evitas llamar múltiples veces a setError() y solo lo llamas una vez pasandole la comparación, su resultado se traduce en true o false
if(value !== SECURITY_CODE){
setError(true);
}else{
setError(false);
}
setLoading(false);
React.useEffect(()=>{
if (loading) {
setTimeout(() => {
setLoading(false)
value !== SECURITY_CODE ? setError(true) : setError(false)
}
,3000)
}
}, [loading])
Yo solo agregue el setError(false) en el eventListener OnChange.
onChange={(event) => {
setValue(event.target.value);
setError(false);
}}
Asi lo hice yo 😃
React.useEffect(()=>{
if (!!loading){
setError(false)
setTimeout(()=>{
if (value === SECURITY_CODE) {
setLoading(false)
setError(false)
} else {
setLoading(false)
setError(true)
}
},500)
}
},[loading])
Mi solución fue envolver todo el código en una función de autenticación, donde por defecto setError cambie a false con cada clic al botón y setLoading a true
Recomiendo la extensión de React para ver los valores de props, y states: En firefox: https://addons.mozilla.org/en-US/firefox/addon/react-devtools/
value === SECURITY_CODE
? setError(false)
: setError(true);
value === SECURITY_CODE
? setLoading(false)
: setLoading(false);
mi solucion
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');
}, 3000);
}
Se puede resolver cambiando el estado del loading en caso de que sea correcta la contraseña o no:
if (value !== SECURITY_CODE) {
setError(true);
} else {
setError(false);
}
setLoading(false);
Le di solución al reto agregando un condicional antes de ingresar al setTimeout que valide si el estado de error se encuentra activado
React.useEffect(() => {
console.log('Effect start');
if (loading) {
console.log('Doing validation');
if (error) {
setError(false)
}
setTimeout(()=>{
if (value != SECURITY_CODE){
setError(true);
}
setLoading(false);
}, 3000)
} else {
console.log('Loading state hasn\'t already changed')
}
console.log('finishing validation');
}, [loading]);
Lo hice de esta manera también para evitar que el estado de error se actualizara de forma iterativa cada vez que el efecto se viera implementado y asegurar que solamente mute el estado cuando sea necesario.
Esta fue mi solucion para el reto del error:
<input
type='text'
placeholder='código de seguridad'
value={value}
onChange={(event)=>{
setValue(event.target.value)
setError(false)
}}
/>
Lo puse en el input ya que me parece lo más lógico.
Aquí mi solución. Al momento de presionar el botón se activa el estado de loading y se desactiva el estado de error. De esta manera si anteriormente hubo un error este desaparecerá, luego, en la validación este validará si el código es el correcto, en caso de que no sea así, activará el estado de error, finalmente, aunque haya coincidido o no el código, el estado de loading se desactivará.
Aqui primero el código del botón.
Aquí el código del efecto.
Realicé la validación para ambos casos, pero con un setTimeout donde indica que está cargando los resultados posteriores, evalúa si es correcto, muestra el mensaje y se vuelve al estado inicial del input. Lo mismo para la condición de error. Código:
import React, { Fragment, useEffect, useState } from 'react';
const PASSWORD = 'react';
function UseState(props){
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const [login, setLogin] = useState('');
const [password, setPassword] = useState(false)
//cargar validacion
const handleClick = () => {
setLoading(true)
}
//login correcto
const loginCorrect = () => {
setTimeout(() => { setLoading(false) }, 2000);
setTimeout(() => setPassword(false), 4000);
setTimeout(() => setPassword(true), 2000);
}
//login incorrecto
const loginIncorrect = () => {
setTimeout( () => { setLoading(false); setError(true) }, 2000);
setTimeout( () => { setError(false) }, 4000)
}
useEffect(() => {
loading ?
setTimeout(() => {
login === PASSWORD ?
loginCorrect()
:
loginIncorrect()})
:
console.log('Validacion Finalizado.')
}, [handleClick]);
return (
<Fragment>
<h2>Eliminar {props.name}</h2>
<p>Por favor, escribe el codigo de seguridad.</p>
<input
type="text"
placeholder='Codigo de seguridad'
value={login}
onChange={(e) => {
setLogin(e.target.value);
}}
/>
<button
onClick={() => handleClick()}
className='btn btn-secondary'
>
Comprobar</button>
{loading && <p className='bg-success'>Validando ...</p>}
{error && <p className='bg-danger text-white'>Error: el codigo no es valido.</p>}
{ password && <h2>Login correcto ✅</h2>}
</Fragment>
);
}
export { UseState };
Yo lo resolvie asi:
./UseState.js
En la “consulta a la API”
setTimeout(
()=>{
console.log("Haciendo la validacion")
setError(value !== SECURITY_CODE);
setLoading(false)
console.log("Terminando la validacion")
},
3000
)
Y en maquetado del componente, especificamente en el mensaje de error:
{(error && !loading) && (
<p>Error: El codigo es incorrecto</p>
)}
Espero les guste mi solucion!
no creo que sea la mas optima, pero si funciona
if( value === SECURITY_CODE ) {
setError(false);
setLoading(false);
} else {
setError(true);
setLoading(false);
}
Lo resolvi con un setTimeout así:
React.useEffect(() => {
console.log("Empezando el Efecto")
if (!!loading) {
setTimeout(() => {
console.log("Haciendo la validación")
if (value === SECURITY_CODE) {
console.log(value)
setLoading(false)
} else {
console.log(value)
setLoading(false)
setError(true)
setTimeout(() => {
setError(false)
}, 1000)
}
console.log("Terminando la validación")
}, 2000);
console.log("Terminando el Efecto")
}
},
[loading]);
Mi solución fue:
Cree un nuevo estado para cuando el código sea válido, iniciado en “false” para mostrar un mensaje cuando sea correcto el código.
cada vez que el usuario presiona el botón para validar este, restaura los estados de error y de válido para limpiar de mensajes el componente.
Dependiendo si el código es correcto o incorrecto, modifico el estado de error o de válido para mostrar el msm correspondiente.
Adicionalmente agregué el método toLowerCase()
para validar el código siempre en minúsculas, independientemente del valor ingresado por el usuario.
Solucion menos indicada pero la mas facil
React.useEffect(()=>{
if(!!loading){
setTimeout(()=>{
console.log('Validando')
if(value === CODE_SECURITY){
setLoading(false)
setError(false)
}else{
setLoading(false)
setError(true)
}
console.log('Final validacion')
},3000);
}
},[loading]);
Mi solución al reto de la clase fue cambiar el estado del error cuando se realiza un cambio en el value del input, ya que aún no se ha realizado la nueva validación, por lo tanto no se conoce si la respuesta tendrá un error:
<input placeholder="Código de seguridad"
value={ value}
onChange={(event) => {
setError(false);
setValue(event.target.value)}}/>
Mi solución. Pero tiene el problema de que siempre muestra un mensaje Aunque se aya recargado la pagina.
{
loading
?<p>Cargando...</p>
:!error
?<p>Aceptado: El codigo es correcto</p>
:<p>Error: El codigo es incorrecto</p
}
MI solución
useEffect(() => {
console.log('Ready effect');
if(loading){
setTimeout(() => {
console.log("Haciendo validación");
if (word !== SECURITY_CODE) {
setError(true);
}
setLoading(false)
console.log("Terminando validación");
},2000)
}
console.log('Finish effect');
},[loading]) /* El componentWillMount seria la priemra carga */
const handleState = () => {
setError(false)
setLoading(true)
}
const handleChange = (e) => {
setError(false)
se
if(!!loading){
setTimeout(()=>{
console.log("Haciendo la validación")
if(value === SECURITY_CODE) {//comentario
setLoading(false);
console.log('acceso permitido')
}else{
setError(true)
setLoading(false)
setTimeout(()=>{
setError(false)
},5000)
}
Les comparto mi solución
También se podría poner en solo dos líneas.
la solucion al reto qu ese me ocurre es colocando un setError(false) antes del setTimeout como podemos ver:
React.useEffect(() => {
console.log("empezando el efecto");
if (!!loading) {
setError(false);
setTimeout(() => {
console.log("haciendo la validacion");
if (value !== SECURITY_CODE) {
setError(true);
}
setLoading(false);
console.log("terminando la validacion");
}, 3000);
}
console.log("terminando el efecto");
}, [loading]);
La otra que se me ocurre es cuando damos click en el boton dar un setError(false).
<button
onClick={() => {
setLoading(true);
setError(false);
}}
>
y por ultimo seria ponerlo en el onChange del input aunque creo que haria que la aplicacion sea poco eficiente lo dejo al juicio de los demas:
<input
placeholder="Codigo de seguridad"
value={value}
onChange={(event) => {
setValue(event.target.value);
setError(false);
}}
/>
Mi solucion es agregar una condición adicional donde se esta mostrando el parrafo, la condición quedaria si error es true pero loading es false muestra el error de esta manera no se muestran los dos parrafos al mismo tiempo. Y adiciono un else para cambiar el estado del error en caso de que coincida la palabra
useEffect(() => {
if (!!loading) {
setTimeout(() => {
if (value !== SECURITY_CODE) {
setError(true);
} else {
setError(false);
}
setLoading(false);
}, 3000);
}
}, [loading]);
return (
<div>
<h2>Eliminar {name}</h2>
<p>Por favor, escribe el código de seguridad.</p>
{error && <p>Error: El código es incorrecto</p>}
{loading && <p>Cargando...</p>}
<input value={value} onChange={(e) => setValue(e.target.value)} placeholder="Código de seguridad" />
<button onClick={() => setLoading(true)}>Comprobar</button>
</div>
);
function UseState({ name }) {
const [value, setValue] = useState('')
const [error, setError] = useState(false)
const [loading, setLoading] = useState(false)
//executes the code when it fulfills the conditions given in the second argument
useEffect(() => {
//validation to avoid execution on page loading
loading &&
setTimeout(() => {
setLoading(false)
value !== SECURITY_CODE ? setError(true) : setError(false)
}, 1000)
}, [loading, value])
return (
<div>
<h2>Delete {name}</h2>
{!!loading ? (
<p>Loading...</p>
) : !error ? (
<p>Please, insert your security code</p>
) : (
<p>Error: incorrect code</p>
)}
<input
placeholder="security code"
value={value}
onChange={({ target }) => setValue(target.value)}
/>
<button onClick={() => setLoading(!loading)}>Check it</button>
</div>
)
}
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])
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?