Handing Loading Flags in Pinia Stores

Brenton Klik
3 min readDec 16, 2022

--

We often use Pinia stores to make requests to our backend APIs through actions. We often set a ‘loading’ flag that can be easily bound to some frontend UI letting the user know they’re waiting on a request. But what happens when your store is making multiple requests? Well, I have a simple design pattern that will help with that.

Imagine we have the following store.

export const useFileSystemStore = defineStore('filesystem', {
state: () => ({
files: {} as FileList,
fileTypes: {} as FileTypeList,
folder: {} as Folder,
folders: {} as FolderList,
loading: false as boolean,
error: '' as string,
}),
getters: {},
actions: {
async getFolders() {
this.loading = true;
await api.get('/api/folders')
.then((response) => {
this.loading = false;
...
})
.catch((error) => {
...
});
},
async getFolder(folderID:string) {
this.loading = true;
await api.get(`/api/folders${folderID}`)
.then((response) => {
this.loading = false;
...
})
.catch((error) => {
...
});
},
async getFileTypes() {
this.loading = true;
await api.get('/api/filetypes')
.then((response) => {
this.loading = false;
...
})
.catch((error) => {
...
});
},
async getFiles(folderID: string) {
this.loading = true;
await api.get(`/api/files${folderID}`)
.then((response) => {
this.loading = false;
...
})
.catch((error) => {
...
});
},
},
});

Our store has a singular purpose of fetching ‘filesystem’ related data; file, folders, etc. Whenever a call to the API is made, we set the ‘loading’ flag to true. On our frontend we can bind something, say a loading spinner, to that flag and the user knows things are happening.

The Problem

The problem with this store is if multiple actions are made at the same time, results from one action will cancel the loading flag for other actions before their results have finished.

Solution One

One way we can handle this is to just make our action calls one at a time.

const fileSystemStore = useFileSystemStore();

fileSystemStore.getFolder(folderID).then(() => {
fileSystemStore.getFiles(folderID);
});

This ensures our loading flag is accurate. However, this is slow. Modern browsers can handle six requests at once to the same host. Wouldn’t it be getter if we took advantage of that?

Solution Two (Better)

Another way to handle this is to use an Array as your loading flag.

export const useFileSystemStore = defineStore('filesystem', {
state: () => ({
files: {} as FileList,
fileTypes: {} as FileTypeList,
folder: {} as Folder,
folders: {} as FolderList,
loadArr: [] as Array<string>,
error: '' as string,
}),
getters: {
loading: (state) => state.loadArray.length > 0,
},
actions: {
async getFolders() {
this.loadArr.push('getFolders');
await api.get('/api/folders')
.then((response) => {
this.loadArray.splice(
this.loadArray.indexOf('getFolders'),
1
);
...
})
.catch((error) => {
...
});
},
async getFolder(folderID:string) {
this.loadArr.push('getFolder');
await api.get(`/api/folders${folderID}`)
.then((response) => {
this.loadArray.splice(
this.loadArray.indexOf('getFolder'),
1
);
...
})
.catch((error) => {
...
});
},
async getFileTypes() {
this.loadArr.push('getFileTypes');
await api.get('/api/filetypes')
.then((response) => {
this.loadArray.splice(
this.loadArray.indexOf('getFileTypes'),
1
);
...
})
.catch((error) => {
...
});
},
async getFiles(folderID: string) {
this.loadArr.push('getFiles');
await api.get(`/api/files${folderID}`)
.then((response) => {
this.loadArray.splice(
this.loadArray.indexOf('getFiles'),
1
);
...
})
.catch((error) => {
...
});
},
},
});

Now, we can make as many action calls as we want at the same time. Anything that was watching the ‘loading’ flag will still work because the ‘loading’ getter we added will stay true until all requests are complete.

When binding to the loading flag on our frontend, we have choices. We can bind to the ‘loading’ getter flag, or bind to our ‘loadArr’ for a specific action!

<template v-if="fileSystemStore.loading">
Loading...
</template>
...
<template v-if="fileSystemStore.loadArr.includes('getFiles')">
Loading Files...
</template>

Conclusion

The load array pattern offers a simple approach to handling loading flags where multiple action requests are happening at once. It makes binding in templates easy and straightforward, and allows you to leverage the browsers ability to manage multiple simultaneous requests.

--

--

Brenton Klik
Brenton Klik

Written by Brenton Klik

Brenton Klik is an Interaction Designer in the Washington DC area. He designs interfaces and experiences for web, mobile, and desktop platforms.