Search code examples
javascriptvue.jsvuejs3reactivepinia

Pinia error: "getActivePinia()" was called but there was no active Pinia. Did you forget to install pinia?


Even though I have an action that changes the 'pending' state depending on if the data was already fetched (which makes it already reactive), when I use it inside of the component, reactivity doesn't work and this is mentioned here in the official Pinia docs: https://pinia.vuejs.org/ssr/#Using-the-store-outside-of-setup-. Then I tried to make a 'pending' state reactive by using storeToRefs, but I keep getting this error:

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.

Here is 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:

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');

and then I'm using 'pending' 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 to fix it?

UPDATE:

The error is gone now, but the 'pending' still acts like it's not reactive. Here, where I pass the props to the child component on skeleton load. For some reason, the skeleton loads infinitely :

<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>

Solution

  • This code needs to be removed from your store

    const store = useStoreRecipes();
    
    export const { pending } = storeToRefs(store);
    

    An interesting idea, but it doesn't work. The pinia instance isn't active yet. Deconstructing refs from your store should really be done in your component code:

    <script setup>
    import { useStoreRecipes } from '@/stores/recipes/storeRecipes.js';
    import { storeToRefs } from 'pinia';
    
    const storeRecipes = useStoreRecipes();
    
    const { pending } = storeToRefs(store);
    </script>