Vue

Store Pinia

Comprendre l'utilité d'un Store dans Vue 3 et comment l'utiliser efficacement avec Pinia

Store

Dans une application Vue, la gestion de l'état peut rapidement devenir complexe, surtout lorsque plusieurs composants ont besoin d'accéder aux mêmes données.

Problème courant sans store

Prenons l'exemple de notre application actuelle. Je souhaite lors d'un click sur un utilisateur de la liste que cela modifie mon compteur pour qu'il soit égal à l'âge de la personne cliquée.

Cela peut être fait via des props et des événements, mais cela devient rapidement ingérable. En effet, il va falloir que les informations remonte du composant <UserCollection /> au composant App.vue pour ensuite être redescendu dans le composant <CounterComponent />.

Ainsi App.vue risque de devenir un fourre-tout de données et de méthodes qui ne le concernent pas directement. Et nous n'avons ici que 3 composants, imaginez une application avec 10, 20 ou 30 composants.

La solution dans notre cas est de créer un outil qui servira à centraliser les données et les méthodes qui les manipulent. Cet outil est appelé Store.

Création d'un Store avec Pinia

Pinia est une solution simple et efficace pour gérer l'état global dans Vue 3.

Installation de Pinia

npm install pinia

Puis, ajoute Pinia à ton application Vue dans main.js :

import './assets/main.css'
import NaiveUI from 'naive-ui';
import {createApp} from 'vue'
import App from './App.vue'
import {createPinia} from 'pinia';

const app = createApp(App);
app.use(NaiveUI);
app.use(createPinia());
app.mount('#app');

Définition d'un Store

Dans notre problématique l'état qui doit être partagé entre les composants est le compteur. Nous allons donc créer un store pour le gérer.

// stores/counter.store.js
import {defineStore} from 'pinia';
import {ref} from 'vue';

export const useCounterStore = defineStore('counter', () => {
    const count = ref(0);

    const reset = () => {
        count.value = 0;
    };

    return {count, reset};
});

Utilisation du Store

Modification du composant Counter.component.vue

<script lang="ts" setup>
  import {computed} from 'vue';
  import {NButton, NCard, NText} from 'naive-ui';
  import {useCounterStore} from "@/stores/counter.store.ts";
  import {storeToRefs} from "pinia";

  const counterStore = useCounterStore();

  const {counter} = storeToRefs(counterStore);

  const doubleCounter = computed(() => counter.value * 2);

</script>s

<template>
  <n-card bordered title="Compteur">
    <n-text strong>Valeur actuelle : {{ counter }}</n-text>
    <br/>
    <n-text type="success">Double : {{ doubleCounter }}</n-text>
    <br/><br/>
    <n-button type="primary" @click="counter++">Incrémenter</n-button>
    <n-button :disabled="counter === 0" class="ml-2" type="error" @click="counterStore.reset()">
      Réinitialiser
    </n-button>
  </n-card>
</template>

Modification de UserCollection.component.vue

<template>
  <n-card bordered title="Liste des utilisateurs">
    <n-list bordered>
      <n-list-item v-for="user in users" :key="user.name" @click="sendUser(user)">
        <n-text>{{ user.name }} - {{ user.age }} ans</n-text>
      </n-list-item>
    </n-list>
    <slot></slot>
  </n-card>
</template>

<script lang="ts" setup>
  import {defineEmits, defineProps} from 'vue';
  import {NCard, NList, NListItem, NText} from 'naive-ui';
  import type {User} from '../types/common.type.ts';
  import {useCounterStore} from "@/stores/counter.store.ts";
  import {storeToRefs} from "pinia";

  const props = defineProps({
    users: {
      type: Array as () => User[],
      required: true,
    },
  });

  const counterStore = useCounterStore();

  const {counter} = storeToRefs(counterStore);

  const emit = defineEmits(['sendUser']);

  const sendUser = (user: User) => {
    counter.value++
    emit('sendUser', user);
  };
</script>