OnClick Quiero llamar a una función que establece mi estado de carga en verdadero, luego hacer un ciclo for que tomará más de un segundo y en el último ciclo establecer el estado de carga nuevamente en falso, pero en mi código el estado de carga no cambia como esperado. ¿Qué tengo que arreglar?
import { useState } from "react" const Test = () => { const [loading, setLoading] = useState(false) const someFunction = () => { setLoading(true) const someVeryBigArray = [...] for (let i = 0; i < someVeryBigArray.length; i++) { if (i === someVeryBigArray.length - 1) { setLoading(false) } } } return ( <button onClick={someFunction} className={`${loading && "text-red-500"}`}> test </button> ) } export default TestDebe darle tiempo al navegador para volver a renderizar. Si tiene un gran bucle de bloqueo, React no devolverá el control al navegador para que pueda volver a pintar (o incluso a sí mismo para que el componente pueda ejecutarse nuevamente con el nuevo estado).
Si bien un enfoque sería ejecutar la función costosa en un gancho de efectos, después de que se haya representado el nuevo estado de carga:
const Test = () => { const [running, setRunning] = useState(false) useEffect(() => { if (!running) return; const someVeryBigArray = [...] for (let i = 0; i < someVeryBigArray.length; i++) { // ... } setRunning(false); }, [running]); return ( <button onClick={() => setRunning(true)} className={running && "text-red-500"}> test </button> ) }Un mejor enfoque sería descargar el código costoso al servidor o a un trabajador web que se ejecuta en un subproceso separado, para no interferir con la vista de la interfaz de usuario que presenta React.
Para ser honesto, si en cualquier caso su bucle tarda 1 segundo en ejecutarse, esto tendrá un costo en el rendimiento de su aplicación. Y esta no es la mejor manera de hacerlo también.
La mejor manera sería, si realmente desea replicar el retraso en su aplicación, entonces debe usar setTimeout() con el que retrasa alguna acción. compartir un fragmento de código podría ayudarlo.
import { useEffect, useState } from "react"; const Test = () => { const [loading, setLoading] = useState(false); let timevar = null; const someFunction = () => { setLoading(true); timevar = setTimeout(() => { setLoading(false); //this will run after 1 second }, 1000); //1000 ms = 1 second }; useEffect(() => { return () => { //clear time out if in case component demounts during the 1 second clearTimeout(timevar); }; }); return ( <button onClick={someFunction} className={`${loading && "text-red-500"}`}> test </button> ); }; export default Test;