Search code examples
javascripttypescriptvuejs3storepinia

Pinia state initialized with async function but only after component is mounted


I'm using Pinia and Vue.js 3 to display a list of products in a page. However, when I use my product store in my component, all I see in the page is loading. Only when I go to another pages and come back to the products page, is when I see that my products are gotten from the store and listed in the page. Which, I guess, it means that the component is mounted and everything before the store has initialized the state from the async function.

Here is the code:

Pinia Store

import { ref, onMounted } from "vue";
import { defineStore } from "pinia";
import { TProduct, TProductResponse } from "@types";

import { fetchData } from "@utils";

export const useProductsStore = defineStore("products", () => {
  const products = ref<TProduct[]>([]);

  async function setProducts() {
    products.value = (
      await fetchData<TProductResponse>(
        "https://dummyjson.com/products?limit=5"
      )
    ).products;
  }

  onMounted(async () => {
    await setProducts();
  });

  return {
    products,
  };
});

Component

<script setup lang="ts">
import { ref } from "vue";

import { ProductCard } from "@components";

import { TProduct } from "@types";

import { useProductsStore } from "@stores";

const productsStore = useProductsStore();

const products = ref<TProduct[]>(productsStore.products);
</script>

Thank You!


Solution

  • The change to the store is being made on initial mount, but the DOM is not refreshing with the updated value because reactivity has been lost. The reason the store values appear after navigating away and back again is because these actions are forcing the DOM to refresh.

    To maintain reactivity in your component you can directly use productsStore.products instead of making a new ref called products. Or if you really want to deconstruct to a new variable you should use the storeToRefs helper function.

    import { useProductsStore } from "@stores";
    import { storeToRefs } from 'pinia'
    
    const productsStore = useProductsStore()
    
    const { products } = storeToRefs(productsStore)
    

    I should also point out that, while it does work in your case, it's not best practice to use onMounted hook inside a Pinia store. Ideally, you should call the setProducts action from an actual component's onMounted hook.