In my app I fetch data from my API after a button is clicked that changes the state of endpoint. Until the data is fetched I want to display a loading icon. After MUCH testing, I finally got it to work. This is what it looks like:
const [endpoint, setEndpoint] = useState('products');
const [loading, setLoading] = useState(true);
const [data, setData] = useState([]);
useEffect(() => {
setLoading(true);
fetch(`/api/${endpoint}`, {
method: 'GET',
})
.then(res => res.json())
.then(data => {
setData(data);
})
.catch(e => {
setData([]);
})
.finally(() => setLoading(false));
}, [endpoint]);
const onSelect = (option) => {
setEndpoint(option);
}
return (
<>
<Sidebar onSelect={onSelect}/>
<div className="main">
<Banner />
{loading ? 'Loading...' : JSON.stringify(data)}
</div>
</>
);
Even though I get the result that I want, I'm a bit confused as to how it all fits together because I'm new to React. Please correct me If I'm wrong, but this is my understanding:
setEndpoint triggers a re-render, which causes useEffect to execute because it has a dependency on endpoint. Each set state call inside useEffect also causes a re-render. For instance when setLoading(true) is called, the screen is re-rendered to show 'Loading...'. Once the state of loading is set to true, fetch gets called. As soon as the promise is resolved, setData(data) causes another re-render. However, my data isn't displayed on screen until setLoading(false) is called, re-rendering the screen yet again, displaying my data. So in total, the screen is re-rendered 3 times (right?).
I'm still confused because I was under the impression that hooks like useEffect are asynchronous so why would it wait for the state to be set before continuing to execute the next lines of code?
loadingis set to true"; the code will continue immediately after the call tosetState.setStateis asynchronous (not in the sense it'sawaitable). Your data isn't displayed untilloadingis false because that's what you told it to do.