In my Vue app I am using several Pinia stores. For most of them, I need to initialize the state with data from a server, i.e. I need to wait for a server response. I'm trying to do this with Setup style stores.
I don't want to explicity call an initialize
function somewhere else in the code. So currently, if I access the state, the store checks if it is initialized and then - if necessary - gets the data from the server.
This looks something like this:
myStore.js
import { defineStore } from "pinia";
import { ref, computed } from "vue";
import myApi from "server";
export const myStore = defineStore("myStore", () => {
const data = ref();
const isInitialized = ref(false);
// Get data from server and set state to initialized. Only get data if the store is not yet initialized.
async function fetchData() {
if (!isInitialized.value) {
const res = await myApi.fetchData();
data.value = res.data;
isInitialized.value = true;
}
}
// Getter for accessing data. First fetch data from server if necessary.
const getData = computed(() => {
fetchData();
return data;
});
return { getData };
});
Now this isn't nice, since computed
should be side-effect free and I don't want to repeat this for all the other getters in this store.
Is there something similar like lifecycle hooks for Pinia stores? Then I could simply directly get the data, as soon as the store is created. If this isn't possible, what would be a better pattern to achieve the same result?
Store initialization is synchronous, not to mention that there would be race conditions when uninitialized data is accessed without a promise to await.
There needs to be explicit initialization method:
async function initialize() {
await myApi.fetchData();
}
Which should be called as early as needed, e.g. in root component with suspense, or with top-level await:
await myStore.initialize()
Another case for initialization method is that store initialization can be parametrized, this makes the usage more flexible and improves testability.