React use useEffect to load data asynchronously
I have a parent component that has a child component inside it which loaded data asynchronously currently I have:
<Parent>
<AsyncChild data={props.data} />
<Child />
</Parent>
Inside my AsyncChild
component I have:
const processDataAsynchronously = data => {
// do stuff with a huge chunk of data
}
export default function AsyncChild(props) {
const data = props.data;
const [dataLoaded, setDataLoaded] = useState(false);
const [myData, setMyData] = useState({});
useEffect(() => {
let mounted = true;
processDataAsynchronously(data);
.then(data => {
if(mounted) {
setMyData(data);
setDataLoaded(true);
}
});
return () => {
mounted = false;
}
}, [data])
const getComponentData = () => {
if(dataLoaded && !myData){
// if data has loaded and is empty return empty
return <span />
} else if (!myData && !dataLoaded) {
// if data is empty and loading is not complete - return loading component
return <Loader />;
} else {
return (
// return style data
)
}
}
return (
<div>
{getComponentData()}
</div>
)
}
What I want to do is to load <Child />
and while <AsyncChild />
is processing it's data asynchronously, display a loader above the <Child />
component. However, currently the loader does not display and both of the child components are displayed together. How can I do this correctly?
Answer
You can add a state into parent component like this:
import { useState } from 'react';
export default function App () {
[isLoading, setLoading] = useState(false);
return (
<Parent>
<AsyncChild data={props.data} setLoading={setLoading}/>
{isLoading && <Loader />}
<Child />
</Parent>
)
}
Then in AsyncChild
you can set the isLoading
in the useEffect
:
useEffect(() => {
setLoading(true); // <----- START LOADING
let mounted = true;
processDataAsynchronously(data);
.then(data => {
if(mounted) {
setMyData(data);
setDataLoaded(true);
}
setLoading(false); // <----- END LOADING
});
return () => {
mounted = false;
}
}, [data])