I have the code that fetches json data from a URL, and displays it, like so:
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} />;
}
Where url is just an arbitrary url that fetches me some data. I have a custom LoadingAnimation function that simply displays loading when the page has not loaded. I pass setUrl down in the BasicLayout function because I want to change the URL parameters with the click of a button and update the data and display it. When the setUrl is triggered, the data updates as it should, with a 4-5 second delay. Now what additionally, while the data is updating during that 4-5 second delay, I want to display a different loading animation on top of all the elements and make the user unable to click on anything while the fetch is updating.
I have tried passing setIsLoaded into BasicLayout but that just replaced the entire screen and re-rendered every component. I especially don't want to re-render anything; just render the loading icon and hide it when everything has finished updating. How would I go about doing that?
at this moment, in your component, if statements return what React have to render. So you got few ways:
in this example if error change React will show BasicLayout or Error message but if isLoaded will change, LoadingAnimation will be hide/shown under the previous element.
useEffect(() => {
...
}, [url, setUrl]);
return (
<>
{error ? <div>Error: {error.message}</div> : <BasicLayout items={items} setUrl={setUrl} />}
{!isLoaded && <LoadingAnimation />}
</>
)
If you'll use right css styles you can simply make LoadingAnimation a modal(pop-up) but for modals, as for me, better to use React Portals
index.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>
)
}
So in your component it'll be something like this
useEffect(() => {
...
}, [url, setUrl]);
return (
<>
{error ?
<div>Error: {error.message}</div>
:
<BasicLayout items={items} setUrl={setUrl} />}
<Modal isActive={!isLoaded} ><LoadingAnimation /></Modal>
</>
)
and you can make error component modal too.
hope, it'll help:))