vue.jsvuejs3pinia

How to correctly handle information fetch from an API with Pinia in Vue.js 3?


I'm currently working on a project where to fetch data from an API. I need access to that data all over my app, so I thought that the best option was to use Pinia (usually I used Vuex, but I want to try this "new" store solution).

My "problem" is that I really don't know if the way I achieve my goal is the best way or even a "good practice". In my Pinia store I wrote this:


export const listadoADPs = defineStore("listado", {
  state: () => ({
    adps: [],
  }),
  actions: {
    getADPs() {
      const api =
        "URL";

      fetch(api)
        .then((response) => response.json())
        .then(({ data }) => (this.adps = data))
        .catch((error) => console.log(error));
    },
  },
});

Then, in my component I coded this:

<script setup>
import { ref, computed } from "vue";
import { listadoADPs } from "@/stores/adps";
const store = listadoADPs();

const cards = ref([
  {
    number: computed(() => store.adps.length),
    description: "ADPs vigentes",
  },
  {
    number: computed(
      () => store.adps.filter((adp) => adp.estado_cd === "Suscrito").length
    ),
    description: "Convenios de Desempeño Suscritos",
  },
  {
    number: 0,
    description: "Alertas enviadas",
  },
]);
</script>

Specifically, I don't know if making a computed property for each "number" key in my array "cards" is right, I mean, finally is the same data, so why I can't make just one computed property and save the data in a variable? The thing is that if I work in that way when I reloaded the page, the data just disappears.

Reading the documentation and much more, I think there is a reactivity issue that I still don't understand at all, but I really want to make well this code, so I prefer to ask to you.


Solution

  • I would use getters here.

    // store.ts
    export const listadoADPs = defineStore("listado", {
      state: () => ({
        adps: [],
      }),
      actions: {
        getADPs() {
          const api =
            "URL";
    
          fetch(api)
            .then((response) => response.json())
            .then(({ data }) => (this.adps = data))
            .catch((error) => console.log(error));
        },
      },
      getters: {
        // BEWARE: getter names cannot be same as state props!
        listadoADPs(state) {
          return state.adps.length;
        },
        adpsFilteredLength(state) {
          return (query: string) => state.adps.filter((adp) => adp.estado_cd === query).length;
        }
      },
    });
    

    in your component

    <script setup>
    import { ref, computed } from "vue";
    import { listadoADPs } from "@/stores/adps";
    const store = listadoADPs();
    
    const cards = ref([
      {
        number: store.adpsLength,
        description: "ADPs vigentes",
      },
      {
        number: computed( // must be computed because of sending parameter
          () => store.adpsFilteredLength("Suscrito")
        ),
        description: "Convenios de Desempeño Suscritos",
      },
      {
        number: 0,
        description: "Alertas enviadas",
      },
    ]);
    </script>
    

    For persistency this Pinia plugin seems to be nicely done: https://www.npmjs.com/package/pinia-plugin-persist

    As others already mentioned I can't help you more without providing more information.

    Idealy post reproduciton of your problem ;)