Introducción a Pinia y Vue 3

Inicial Vue Curso Vue en Escuela Vue

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 state desde 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); // ✅

Demo

Conclusión

  • Ya ves lo sencillo que es dar los primeros pasos con Pinia y Vue 3.
  • Como siempre, aquí tienes el enlace al código/demo.
  • Happy coding 🔥
Código asociado
Curso Vue en Escuela Vue

¿Tienes alguna pregunta sobre Introducción a Pinia y Vue 3?

Resuelve todas tus dudas sobre Vue en la Comunidad de Escuela Vue: un lugar donde participar, aprender y ayudar. ¡Te esperamos!.

Tras el registro (si no lo has hecho ya) serás redirigido/a al canal adecuado en la Comunidad.

Twitter

Sigue el día a día y todo lo relacionado con Escuela Vue a través de su cuenta de Twitter.

Discord

En la Comunidad de Escuela Vue podrás solucionar tus dudas y ayudar a otras personas como tú a solucionar las suyas.

Recibe novedades en tu 📬