Almacenando datos con Vuex

Este es el cuarto tutorial de la serie Vuejs desde el CDN en el cual aprenderemos los principios de Vuex, una fantástica herramienta de Vuejs que nos ayudará en el almacenamiento de datos y la comunicación entre componentes.

Índice

  1. Qué es Vuex
  2. Datos globales con vuex
  3. Métodos globales con vuex
  4. Llamando a un dato con mapState
  5. Llamando a un método con mapMutations
  6. Tu turno
  7. El código

1. Qué es Vuex

Vuex es una librería de vuejs que implementa ciertas utilidades que usan un patrón especial que nos permite centralizar el almacenamiento para todos nuestros componentes. Si no utilizamos Vuex la comunicación entre los datos de un componente a otro tiene que hacer el siguiente recorrido.

Por este motivo usar Vuex es fundamental en aplicaciones complejas que usan muchos componentes. Vuex nos provee de un lugar donde almacenar la información, a este lugar se le conoce como estado o state. Si algún componente actualiza el state de Vuex el otro componente también lo sabrá y podrá acceder a la información actualizada desde ahí.

Sin embargo, esto trae consigo un problema. El cambio de estado se puede volver intrazable e impredecible por lo que Vuex implementa un patrón para estandarizar la forma de hacer cambios en el estado de Vuex. Este patrón tiene tres elementos que nos permitirán administrar el estado de Vuex. Estos son los Mutations, Actions y Getters. En este tutorial hablaremos de los Mutations y cómo estos cambian los datos en el state de Vuex.

Pero antes de todo, para utilizar Vuex primero debemos instalarla dentro de nuestro proyecto HTML utilizando su CDN que encontramos en la documentación oficial. Ten en cuenta que el CDN de Vuex debe estar después del CDN de Vue.

2. Datos globales con vuex

Para poder usar Vuex debemos instanciarlo utilizando el método Store el cual recibirá como parámetro un objeto con las propiedades de Vuex. Por ejemplo, dentro de la propiedad state colocaremos las variables que queremos utilizar en todo nuestro proyecto.

Es importante que la instancia de Vuex esté antes de la instancia de Vue.
const almacen = new Vuex.Store({
  state: {
    numero: 10,
  },
});

Para que nuestra instancia de Vuex sea reconocida por la instancia de Vue debemos referenciarla dentro de la propiedad store de la instancia de Vue, como se muestra a continuación.

new Vue({
  el: "#app",
  store: almacen,
});

Luego de realizar esta referencia nuestros datos dentro del state de Vuex ya estarán disponibles para cualquier componente de nuestro proyecto solo debemos llamarlos de la siguiente forma $store.state.variable.

<h1>{{ $store.state.numero }}</h1>

Es importante mencionar que $store.state.variable solo se usa dentro de un template si queremos llamarlo desde una función del computed o methods tenemos que usar almacen.state.variable.

3. Métodos globales con vuex

Vuex también nos permite crear métodos que estarán disponibles en cualquier parte de nuestro proyecto. Estos métodos se llaman mutations. Podemos declarar estos métodos de la siguiente forma dentro de nuestra instancia de Vuex.

const almacen = new Vuex.Store({
  state: {
    numero: 10,
  },
  mutations: {
    agregar1: function () {
      this.state.numero++;
    },
    agregar2: function (state) {
      state.numero++;
    },
  },
});

Como puedes ver en el código anterior hay dos formas de llamar a un dato del state. Podemos usar el this o pasarle como parámetro el state de Vuex para obtener los datos mediante este.

Para llamar a un mutation de Vuex dentro de nuestro template tenemos que hacer uso del método commit() al cual le pasaremos el mutation que queremos "cometer" o "perpetrar".

<button @click="$store.commit('agregar2')">+</button>
Si usamos el parametro state dentro de un mutation este no se envia ni se menciona al momento de llamarlo desde el HTML.

4. Llamando a un dato con mapState

Para poder llamar a un dato de Vuex usamos $store.state.numero, además si queremos llamarlo desde fuera usamos almacen.state.numero. Una forma de solucionar este problema sería creando un método computed que implemente todo el código para llamar a las variables del estado de Vuex. De esta forma nosotros solo llamamos al computed.

...
computed:{
    numero(){
        return almacen.state.numero;
    }
}
... })

Esto podría ayudar pero Vuex nos ofrece una forma más rápida de hacerlo y es mapeando el estado de Vuex dentro de cada componente. Este mapeo debe hacerse dentro del computed.

Sin embargo, debemos saber que no será posible usar Vuex.mapState(), de manera directa en el computed ya que internamente este método es un objeto y usarlo sería como hacer lo siguiente.

computed:{
    numero(){
        return almacen.state.numero
    },
    {
        datoA: '1',
        datoB: '2',
    }
}

Esto es imposible de realizar en JavaScript. Por este motivo Vuex en su documentación nos brinda otra forma de hacer esto usando una característica propia de EcmaScript 6. Usando las propiedades Rest/Spread de Javascript podemos mezclar objetos con un resultado espectacular. Mira el siguiente ejemplo de Javascript.

let z = {
  tercero: 3,
  cuarto: 4,
};
let total = {
  primero: 1,
  segundo: 2,
  ...z,
};

console.log(total);
/*
{
    primero: 1,
    segundo: 2,
    tercero: 3,
    cuarto: 4
}
*/

Si nos damos cuenta ...z contiene un objeto con varias propiedades y gracias a ... podemos mezclar un objeto con el otro. Esto es lo mismo que utilizaremos aquí. Nuestro Vuex.mapState() contiene los elementos traídos del state de vuex y lo que queremos es mezclarlos con el objeto general del computed para poder utilizarlos directamente.

...
computed:{
    calculo(){
        // Algún cálculo
    },
    ...Vuex.mapState(['numero'])
}
... })

Dentro del arreglo de mapState() debemos colocar el nombre de los datos del state de vuex que queremos usar dentro de este componente. Y listo!! ahora los datos ya son accesibles usando solo su nombre.

<h3>{{ numero }}</h3>

Recuerda que esta propiedad computed Vuex.mapState() se debe crear en cada componente solo nombrando a los datos que vamos a usar en el componente, de esta manera por seguridad no exponemos otros datos en el componente.

5. Llamando a un método con mapMutations

Podemos hacer lo mismo que con los datos pero ahora con los mutations usando el mapMutations(). Pero, el mapMutation debemos declararlo dentro de la propiedad methods.

Vue.component("hijo", {
  template: `
    <div>
        <button @click="agregar()">+</button>
    </div>
    `,
  methods: {
    ...Vuex.mapMutations(["agregar"]),
  },
});
Cuando llamamos a un método desde la propiedad methods es necesario el uso de parentesis al final, al momento de llamarlos desde el template.

Muchos mutations tendrán la necesidad de aceptar parámetros y además el parámetro state será frecuente si usamos datos del state en su interior. Por este motivo, al llamar a un mutation siempre se obvia el parámetro state solo enviamos los demás parámetros, como en el siguiente ejemplo.

mutations:{
    agregar: function(state, num) {
        state.numero = num
    }
}
Vue.component("hijo", {
  template: `
    <div>
        <button @click="agregar(2)">+</button>
    </div>
    `,
  methods: {
    ...Vuex.mapMutations(["agregar"]),
  },
});

6. Tu turno

Usando estos conceptos que acabamos de aprender de Vuejs y Vuex te animo a realizar el siguiente proyecto. Esta es una aplicación web cuya estructura está segmentada en diferentes componentes. La web tiene un formulario donde podrás colocar un nombre y al aceptar todos los componentes cambiaran de nombre al nuevo nombre que pusiste en el formulario. Puedes probar la aplicación ya construida en andygeek.github.io/EjemploWebAppVuex.

Cuando quieras puedes ver el código del proyecto terminado. Pero ándale inténtalo tu mismo!!

7. El código

El código del proyecto está en el siguiente repositorio github.com/andygeek/EjemploWebAppVuex