Tengo el código que obtiene datos json de una URL y los muestra, así:
const [error, setError] = useState(null); const [isLoaded, setIsLoaded] = useState(false); const [items, setItems] = useState([]); const [url, setUrl] = useState(url); useEffect(() => { fetch(url) .then(res => res.json()) .then( (result) => { setIsLoaded(true); setItems(result); }, // Note: it's important to handle errors here instead of a catch() block // so that we don't swallow exceptions from actual bugs in components. (error) => { setIsLoaded(true); setError(error); } ) }, [url, setUrl]); if (error) { return <div>Error: {error.message}</div>; } else if (!isLoaded) { return <LoadingAnimation />; } else { return <BasicLayout items={items} setUrl={setUrl} />; } Donde url es solo una url arbitraria que me obtiene algunos datos. Tengo una función LoadingAnimation personalizada que simplemente muestra la carga cuando la página no se ha cargado. Paso setUrl en la función BasicLayout porque quiero cambiar los parámetros de URL con el clic de un botón y actualizar los datos y mostrarlos. Cuando se activa setUrl, los datos se actualizan como deberían, con un retraso de 4 a 5 segundos. Ahora, además, mientras los datos se actualizan durante ese retraso de 4-5 segundos, quiero mostrar una animación de carga diferente encima de todos los elementos y hacer que el usuario no pueda hacer clic en nada mientras se actualiza la búsqueda.
Intenté pasar setIsLoaded a BasicLayout pero eso solo reemplazó la pantalla completa y volvió a renderizar cada componente. Especialmente no quiero volver a renderizar nada; simplemente represente el ícono de carga y ocúltelo cuando todo haya terminado de actualizarse. ¿Cómo haría para hacer eso?
en este momento, en su componente, if las declaraciones return lo que React tiene que representar. Así que tienes algunas formas:
en este ejemplo, si hay un cambio de error , React mostrará BasicLayout o un mensaje de error, pero si isLoaded cambiará, LoadingAnimation se ocultará/se mostrará debajo del elemento anterior.
useEffect(() => { ... }, [url, setUrl]); return ( <> {error ? <div>Error: {error.message}</div> : <BasicLayout items={items} setUrl={setUrl} />} {!isLoaded && <LoadingAnimation />} </> )Si usa los estilos css correctos, simplemente puede hacer que LoadingAnimation sea un modal (ventana emergente), pero para los modales, en cuanto a mí, es mejor usar React Portals
índice.html
<div id='root' ></div> <div id='modal' ></div>Portal.jsx
const Portal = ({children}) => { const portalNode = document.getElementById('modal') return createPortal(children, portalNode) }Modal.jsx
const Modal = ({isActive, setIsActive, children}) => { return ( <Portal> {isActive && <div>{/* ...some code with children */}</div>} </Portal> ) }Así que en tu componente será algo como esto
useEffect(() => { ... }, [url, setUrl]); return ( <> {error ? <div>Error: {error.message}</div> : <BasicLayout items={items} setUrl={setUrl} />} <Modal isActive={!isLoaded} ><LoadingAnimation /></Modal> </> )y también puede hacer que el componente de error sea modal.
Espero que ayude :))