Despite having an action that dynamically updates the 'pending' state based on whether the data has been fetched, reactivity seems to be non-functional when used inside the component. This issue is referenced in the official Pinia documentation: . Attempting to make the 'pending' state reactive using storeToRefs resulted in the following error message:
Uncaught Error: [🍍]: "getActivePinia()" was called but there was no active Pinia. Did you forget to install pinia?
const pinia = createPinia()
app.use(pinia)
This will fail in production.
The code snippet for my Pinia store:
import { defineStore, storeToRefs } from 'pinia';
export const useStoreRecipes = defineStore('storeRecipes', {
state: () => {
return {
data: [],
pending: false,
};
},
actions: {
async loadRecipes() {
try {
this.pending = true;
const res = await fetch('/api/recipe');
if (res.ok) {
const data = await res.json();
this.data = data;
} else {
console.error('Error: ', res.status, res.statusText);
}
} catch (err) {
console.error(err);
} finally {
this.pending = false;
}
},
},
});
const store = useStoreRecipes();
export const { pending } = storeToRefs(store);
My main.js file:
import '@/assets/scss/main.scss';
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import router from '@/router';
import App from './App.vue';
const app = createApp(App);
const pinia = createPinia();
app.use(pinia);
app.use(router);
app.mount('#app');
I then utilize the 'pending' property in one of my components.
<template>
<Swiper
class="swiper"
:breakpoints="swiperOptions.breakpoints"
:pagination="{
clickable: true,
}"
:loop="true"
:modules="swiperOptions.modules">
<template v-for="recipe in storeRecipes.data" :key="recipe.id">
<SwiperSlide class="swiper__slide">
<ItemCard :data="recipe" :pending="storeRecipes.pending" />
</SwiperSlide>
<div class="swiper-custom-pagination"></div>
</template>
</Swiper>
</template>
<script setup>
import { onMounted } from 'vue';
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Pagination } from 'swiper/modules';
import 'swiper/css/free-mode';
import 'swiper/css/pagination';
import 'swiper/css';
import { useStoreRecipes } from '@/stores/recipes/storeRecipes.js';
import ItemCard from '@/components/ItemCard.vue';
const storeRecipes = useStoreRecipes();
onMounted(async () => {
await storeRecipes.loadRecipes();
});
</script>
How can I resolve this issue?
UPDATE:
The error has been resolved, but the 'pending' state still doesn't behave reactively. In this scenario where props are passed to the child component during skeleton loading, the skeleton loads continuously:
<template>
<div class="card">
<div class="card__item">
<ItemCardSkeleton v-if="!pending" />
<template v-else>
<img
class="card__image"
:src="getSrc('.jpg')"
:alt="data.alt"
width="15.625rem" />
<div class="card__content">
<h2 class="card__title">{{ data.title }}</h2>
<p class="card__text">{{ data.text }}</p>
<router-link class="card__link" :to="{ name: 'Home' }"
>View more</router-link
>
</div>
</template>
</div>
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue';
import ItemCardSkeleton from '@/components/SkeletonLoaders/ItemCardSkeleton.vue';
const props = defineProps(['data', 'pending']);
const isLoaded = ref(false);
const getSrc = ext => {
return new URL(
`../assets/images/recipe/${props.data.image}${ext}`,
import.meta.url
).href;
};
onMounted(() => {
const img = new Image(getSrc('.jpg'));
img.onload = () => {
isLoaded.value = true;
};
img.src = getSrc('.jpg');
});
</script>