No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Estados independientes con useState

6/19
Recursos

Aportes 115

Preguntas 3

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

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])
![](https://static.platzi.com/media/user_upload/image-578cd27f-40b4-4084-a3a4-549aa8e4cd37.jpg) cada vez que se oprima el boton de comprobar, que sete el error a false
Mi solución es la siguiente: No es necesario utilizar un if para comprobar si hay error o no, tan solo sería necesario añadir la siguiente linea de código para "settear" el valor del estado de error: ```js setError(value!==SECURITY_CODE) ``` ¡Con esto ya estaríamos listos!
En el useEffect cuando es correcta la respuesta nuestro setError pasa a ser falso, y para mostrarlo es cuando no este cargando !loading && nuestro error sea true ![](https://static.platzi.com/media/user_upload/image-70a93099-f243-4ca7-92c5-6a9cfe91fca8.jpg)![](https://static.platzi.com/media/user_upload/image-55779e18-a3c4-45a4-86fc-e53b9e5ff48b.jpg)
\<button onClick={()=> {setLoading(true)              setError(false)            }                          }>Comprobar\</button> lo que hice fue que cada vez que oprimo el boton de comprobar, sete el error a false y con eso se soluciona
Se puede validar de la siguiente forma, fácil y rápido. ```js useEffect(()=>{ console.log("first efecto") if(!!loading){ setError(false); setTimeout(()=>{ if(value !== SECURITY_CODE){ setError(true) } else{ setError(false) } console.log("valor correcto", value) setLoading(false) }, 3000) } }, [loading]); ```  useEffect(()=>{        console.log("first efecto")     if(!!loading){      setError(false);      setTimeout(()=>{        if(value !== SECURITY\_CODE){          setError(true)        }        else{          setError(false)        }        console.log("valor correcto", value)        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);
    }
Creo que bastaría con colocar serError(false) en el onChange del input.. Así el render "limpiaría" el error cuando el usuario decida intentar otra entrada. Pero veo la diferencia con usar las clases, en cuyo caso el render sería cuando cambie cualquier valor del estado ya que éste es un único objeto global.

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:

  1. 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.

  2. 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.

  3. Dependiendo si el código es correcto o incorrecto, modifico el estado de error o de válido para mostrar el msm correspondiente.

  4. 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])

Mi solución al reto:

        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])
Mi solución fue escuchar el onchange del input con UseEffect y setear el error como falso : ```js useEffect(() =>{ if (valueInput.length > 0) { setError(false) } },[valueInput]) ```    useEffect(() =>{        if (valueInput.length > 0) {            setError(false)        }    },\[valueInput])
Lo hice de esta manera y funciono ` onChange={(event) =>{ setValue(event.target.value); setError(false); } }/>`
Esta práctica la aprendí de los 24 Retos de Navidad de JS => gatitos anti spoiler para que toda alumna o alumno pueda disfrutar de resolver el reto: ฅ^•ﻌ•^ฅ ฅ^•ﻌ•^ฅ ฅ^•ﻌ•^ฅ ฅ^•ﻌ•^ฅ ฅ^•ﻌ•^ฅ ฅ^•ﻌ•^ฅ ฅ^•ﻌ•^ฅ ฅ^•ﻌ•^ฅ ฅ^•ﻌ•^ฅ Mis soluciones: ```js 1 Jugar con el setTimeout if(loading) { setTimeout(() => { console.log('Hace valiacion'); if ( value !== SECURITY_CODE ) { setError(true); } setLoading(false); console.log('Termina valiacion'); }, 1800) setTimeout(() => { setError(false); }, 3900) } 2. poner un setError(false) en el onchange <input placeholder='Código de seguridad' value={value} onChange={(e) => { setError(false) setValue(e.target.value) }} /> 3. colocar un setError(false) en el onClick del button <button onClick={() => { setError(false) setLoading(true) }} > ```
Mi solución: ```js useEffect(() => { if (!!loading) { setTimeout(() => { if (value !== SECURITY_CODE) { setError(true); } setLoading(false); }, 2000); } }, [loading]); <button onClick={() => { setError(false); setLoading(true); }} > Comprobar </button> ```