Si has trabajado con Vue 2 seguro que habrás usado Vuex: la solución oficial de Vue para el manejo de estado.
Ahora, con Vue 3 y su Composition API, no tenemos nueva versión de Vuex sino su reemplazo: Pinia. En este DevTip aprenderás a dar los primeros pasos con Pinia.
¿Pero, qué es Pinia exactamente?.
¿Qué es Pinia?
Pinia elimina gran parte de la complejidad de Vuex en varias áreas:
- Ya no existen módulos como tal, sino diferentes stores (como las hojas de una piña 🍍) que se pueden comunicar facilmente.
- No existen mutaciones porque no son necesarias para llevar el control de los cambios de estado (con la API de
reactividad de Vue 3 expuesta, podemos mutar statedesde cualquier lugar).
- Es mucho más sencillo importar nuestras stores en cualquier componente o composable.
- Mejor soporte (por defecto) para TypeScript.
- Soporte nativo para Vue DevTools 🔥.
- Extremadamente ligera: alrededor de 1.5kb.
Te sugiero que le eches un vistazo a la documentación oficial de Pinia.
Usando Vue 3 y Pinia
Instalación de Pinia
Lo primero, por supuesto, es instalar Pinia y añadirla a nuestro setup.
Así que yarn add pinia y luego:
// main.js
import {createApp} from 'vue'
import {createPinia} from 'pinia'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
    Crear una store con Pinia
Ahora podemos crear nuestra store, por ejemplo, en stores/counter.js. Aquí haremos uso del método de
Pinia defineStore para crear una nueva store con al menos la propiedad options para el estado, una
acción increment para mutarla y un getter totalClicks que nos devolverá un valor computado.
// stores/counter.js
import {defineStore} from 'pinia';
export const useCounterStore = defineStore('counter', {
    state: () => ({
        options: {
            a: 0,
            b: 0,
            c: 0,
        },
    }),
    actions: {
        increment(option) {
            this.options[option]++;
        },
    },
    getters: {
        totalClicks() {
            return Object.values(this.options).reduce((total, current) => {
                return total + current;
            }, 0);
        },
    },
});
    Utilizando una store creada con Pinia
Para usar una store creada con Pinia solo tenemos que importarla en cualquier componente
o composable Vue 3, así que vamos a
importar useCounterStore para luego ejecutar la función que nos devolverá la store.
Guardamos todo ello en la constante store.
<!-- Component.vue -->
<script setup>
import { useCounterStore } from '../stores/counter';
const store = useCounterStore();
</script>
    Ahora podemos hacer uso de sus propiedades: estado, acciones y getters. Al usar la sintaxis script setup tenemos
acceso directo a todo ello en el template.
Fíjate como interpolamos el getter totalCkicks (reactivo, por supuesto), mostramos el valor de cada propiedad
de options con store.options[option] y llamamos a la acción store.increment[option] como si fuera un método más (
lo es).
<!-- Component.vue -->
<template>
  <h2>Total Clics: {{ store.totalClicks }}</h2>
  <div class="card">
    <button type="button" @click="store.increment('a')">
      Option A clicked {{ store.options['a'] }} times
    </button>
    <button type="button" @click="store.delayedIncrement('b')">
      Option B clicked {{ store.options['b'] }} times
    </button>
    <button type="button" @click="store.increment('c')">
      Option C clicked {{ store.options['c'] }} times
    </button>
  </div>
</template>
    Acciones asíncronas con Pinia
Igual que con Vuex, las acciones de Pinia también puede ser asíncronas. Imagina un caso en la que realizas una petición HTTP y tienes que esperar a obtener la respuesta para actualizar el estado o llamar a otras acciones.
Vamos a crear delayedIncrement para que espere una cantidad de milisegundos antes de actualizar options.
Tan sencillo como esto:
// stores/counter.js
const wait = (time = 1000) =>
  new Promise((resolve) => setTimeout(resolve, time));
export const useCounterStore = defineStore('counter', {
  ...
  actions: {
    ...
        async delayedIncrement(option) {
        await wait();
        this.options[option]++;
    },
  },
    ...
});
    Desestructurar Pinia con storeToRefs
Igual que con la API reactive de Vue 3, no podemos
desestructurar los valores resultantes al instanciar nuestra store. Esto no funcionará:
const store = useCounterStore();
const { options, increment, totalClicks } = useCounterStore(); // ❌
    Para evitar perder la reactividad en tu store usa el helper de Pinia storeToRefs:
import {storeToRefs} from 'pinia'
const store = useCounterStore();
const {options, increment, totalClicks} = storeToRefs(store); // ✅
    Conclusión
- Ya ves lo sencillo que es dar los primeros pasos con Pinia y Vue 3.
- Happy coding 🔥
