Most of the loading screens I saw, based on boolean variable loading
. Then based on it, component loadingScreen
is returned or the actual page. The more data I wanted to load, the more if statements
I had to make to check if I am still loading. More such ifs sounds like a bad idea to me 🤷♀️.
I wanted to make my LoadingScreen
component smart enough to figure out, if it should be still displayed.
Let's keep loading screen simple for this example. If it has children, display them. Else, use default loader.
// LoadingScreen.js
const LoadingScreen = (props) => {
return (
<div class="bg-gray-600 h-screen w-screen fixed">
{
props.children? (
<div>
{props.children}
</div>
):(
<div>Default loader...</div>
)
}
</div>
)
}
Since loader has to decide if data is already loaded, it needs to have access to those data. From the main component point of view it will look like this.
// MainPage.js
const MainPage = (props) => {
const [data, setData] = useState(undefined);
useEffect(() => {
if(typeof props.data !== 'undefined'){
var keyValuePairs = Object.values(props.data).map((key) => <li key={key}>{key}</li>);
setData(keyValuePairs);
}else{
props.makeRequest();
}
}, [props.data])
return (
<>
<LoadingScreen toLoad={[data]}/>
<div>
<h2>Conent:</h2>
<div>
{data}
</div>
</div>
</>
)
}
const mapStateToProps = (state) => {
return {
data: state.main.data
}
}
const mapDispatchToProps = dispatch => ({
makeRequest: () => dispatch(getData());
})
The simplest way of checking if data is already loaded, is checking if all elements in array toLoad
are not undefined
.
Let's add such check to LoadingScreen
component.
// LoadingScreen.js
const LoadingScreen = (props) => {
const isDataLoaded = () => {
for(var i in props.toLoad){
if(typeof props.toLoad[i] === 'undefined'){
return false;
}
}
return true;
}
return (
isDataLoaded() ? (
null
):(
<div class="bg-gray-600 h-screen w-screen fixed">
{
props.children? (
<div>
{props.children}
</div>
):(
<div>Default loader...</div>
)
}
</div>
)
)
}
And that's it! LoadingScreen
will be displayed till data will stay undefined
. Another approach is to check if data is equal to it's initial state.
// MainPage.js
<LoadingScreen toLoad={[data]} toLoadInitialState={[initialData]}/>
And the check will be:
// LoadingScreen.js
const isDataLoaded = () => {
for(var i in props.toLoad){
if(props.toLoad[i] === props.toLoadInitialState[i]){
return false;
}
}
return true;
}
Of course the problem will be when obtained data will be equal initial data but in most of my cases it does the job.
It is about one month since I started to learn React so feel free to point out any rookie mistake I made 😉.
Top comments (6)
I'm not sure why you'd need to check individual data points to know whether data has loaded. You're conflating: (1) checking whether data has been loaded, and (2) checking whether the data is valid (contains all expected properties).
I think you've come up with an incomplete pattern for data validation. You'd need to add in some checks for data types and value constraints rather than only checking whether a property exists. However, to simply check whether data has been loaded, all you really need to know is whether your method for loading the data (or, usually, fetching the data) has completed or not.
It's important to understand that the question of whether data has been loaded is a different question from whether data is valid.
Here's an example of a solid React + Redux implementation I threw together: codepen.io/HoraceShmorace/pen/QWpxLYw. It determines whether data is loading in a
loadData
Redux action, and determines whether data is valid in asetData
Redux action.Thanks for your example. I thought it over again and you are totally right. Proper validating data in loading screen component leads to as much 'if's' as cheking if the data is loaded in parent component and should be separate from checking if fetching is finished.
I was trying to find a way of checking if fetching is finished with Promisse.all( ) but your solution with two Redux actions is much simpler and cleaner 😉.
I think it is a good idea to extend this post with your solution. Whould you like to make your own post about it and I will link to it? I can sum up your solution below this one too.
You don't really need 2 Redux actions. I really just used a second one to make it easier to simulate invalid data. The point here is that: (1) checking that your data has loaded and (2) checking that the data is valid are two different things, both of which you should do, but not at the same time.
Here's a slightly different example that illustrates a better pattern codepen.io/HoraceShmorace/pen/yLMqOVr. Note the
validateData
function, how validation was moved into theloadData
async action, and that thesetData
async action was removed entirely.What if the backend returns an error. Then the data will be undefined still, in this case the loader will be shown indefinitely.
I would catch this error and pass it to the state. Then handle it only in LoadingScreen component. Add some message on loadingScreen that something went wrong or redirect. What do you think? 😉
Yeah nice idea.