One of the daily tasks of every frontend developer is downloading data from the server. During the download, you usually need to show user a spinner or an error message if download fails. In code, loading status is often described with boolean flags:
const comments = {
isLoading: false,
isLoaded: false,
isError: false,
errorText: '',
data: [],
};
At first glance, these flags seem convenient. With such approach data loading code looks something like this:
const comments = {
isLoading: false,
isLoaded: false,
isError: false,
errorText: '',
data: [],
};
function loadComments() {
comments.isLoaded = false;
comments.isError = false;
comments.isLoading = true;
fetch('/comments')
.then((response) => response.json())
.then((data) => {
comments.isLoading = false;
comments.isLoaded = true;
comments.data = data;
})
.catch((error) => {
commments.isLoading = false;
comments.isError = true;
comments.errorText = error.message;
});
}
It turns out to be glibly and confusing. You have to be careful resetting flags, otherwise you’ll get meaningless flag combinations like isLoading: true
and isLoaded: true
.
A more practical approach is to describe the state with single dataState
field and automatically calculate flags based on the value of this field:
const dataStates = {
notAsked: 'notAsked',
loading: 'loading',
loaded: 'loaded',
failed: 'failed',
};
const comments = {
dataState: dataStates.notAsked,
errorText: '',
data: [],
get isLoading() {
return this.dataState === dataStates.loading;
},
get isLoaded() {
return this.dataState === dataStates.loaded;
},
get isError() {
return this.dataState === dataStates.failed;
},
};
The code has grown a bit, but as a result all flags change automatically, and the probability of error due to incorrect flag update is reduced to zero. The comments uploading function becomes much simpler:
function loadComments() {
comments.dataState = dataStates.loading;
fetch('/comments')
.then((response) => response.json())
.then((data) => {
comments.dataState = dataStates.loaded;
comments.data = data;
})
.catch((error) => {
comments.dataState = dataStates.failed;
comments.errorText = error.message;
});
}