En el siguiente artículo vamos a profundizar sobre ¿qué es Vuex? y sus conceptos básicos, que nos ofrece, cuales son sus ventajas y desventajas y porque utilizarlo para manejar un estado global en nuestra aplicación con Vue.js


Antes que nada:

Si no tenes conocimientos previos en Vue, primero te recomiendo pasar por los siguientes posts antes de continuar con la lectura, para conocer la libreria y que nos ofrece.

Empezando con Vue.js
Vue : ciclo de vida de un componente
Creación de componentes con Options Api en Vue


¿Qué es Vuex?


Vuex es una biblioteca y un gestor de estados sumamente popular para aplicaciones Vue.js, inspirada en Flux , Redux y The Elm Architecture y con reglas que garantizan que el estado solo se puede modificar de forma predecible.
Trabaja tanto con el optionsApi como con el compositionApi (aunque en este último hay que tener algunas consideraciones, como por ejemplo tener la versión 4 de vuex) similar a como lo hace Vue. En vez de tener un objeto reactivo dentro de la propiedad “data” de nuestros componentes, Vuex maneja este objeto reactivo dentro de un store, alojado bien alto en nuestra aplicación para que cualquier componente pueda acceder a este objeto en caso de ser requerido, y cuando este se vea actualizado, se actualizarán también los componentes de nuestra aplicación que hagan uso de él.
Nos facilita mucho la comunicación entre componentes, sobre todo cuando son componentes que requieren acceder a la misma información para trabajar, pero en el árbol de componentes están muy alejados entre sí.
También es muy útil cuando estamos usando un router, ya que este agrega una capa más de complejidad a la hora de la comunicación entre nuestros componentes.
Otro motivo por el cual podemos necesitar el utilizar Vuex es cuando nuestra aplicación tiene muchos componentes, ya que la información fluye en tantas direcciones que es muy difícil saber en donde se encuentra la fuente actualizada, o en donde podemos obtener la misma, entre otras cosas.


Profundicemos aún más en como funciona Vuex.


Para entender mejor el cómo Vuex funciona debemos tener en claro ciertos conceptos como: Store, Actions, Mutations, State, Getters.

Las Actions son funciones que pueden o no ser asíncronas y que llaman a las mutations una vez cumplan su objetivo, para poder actualizar el state. Normalmente estas actions serán llamadas desde nuestros componentes cuando queramos modificar el estado de nuestro Store. A tener en cuenta que las actions no modifican directamente el estado, sino que realizan un commit en los mutations, y serán estos los encargados de modificarlo. Estos commits siempre deben de ser síncronos.
Un ejemplo de una actions podría ser el llamado a una api para obtener cierta información que se necesita para modificar el estado.

Las Mutations son las funciones que realizan la modificación de nuestro estado global en nuestra aplicación. Estas pueden ser llamadas directamente desde nuestros componentes sin necesidad de que haya una action de por medio, o a través de las mismas

El State es similar al objeto reactivo que manejamos dentro de la propiedad “data” de nuestros componentes, solo que se encuentra a un nivel más alto, dentro de nuestro store, para poder ser accedido por cualquier componente que lo requiera y que sirve como la "única fuente de verdad”.

Los Getters son similares a las propiedades computadas de nuestros componentes Vue pero para el store, y que reciben como primer parámetro el estado.

El Store es como un contenedor, el cual puede tener dentro nuestro state, getters, actions, mutations y distintos módulos, o incluso bien puede constar solo de módulos, los cuales dentro tendrán un state, getters, actions y mutations.

Ya que Vuex agrega complejidad a nuestra aplicación que usualmente será necesaria cuando la misma crezca, es una buena idea trabajar en módulos que separen en porciones de lógica más pequeñas nuestra aplicación y no manejar todo desde un gran módulo global, ya que esto nos va a facilitar mucho la tarea de mantener nuestro código, sobre todo cuando la aplicación sea grande. Básicamente, estos módulos, son agrupadores donde se almacenan el state, getters, actions y mutations.


Y ¿Cómo es el flujo en Vuex?

El flujo de Vuex es de la siguiente manera. Nuestras vistas o componentes de Vue harán un dispatch de una Action, que en caso de ser una función asíncrona se esperará a que la promesa se resuelva para luego hacer un commit con una Mutations, este mutara a nuestro State, y nuestro componente será notificado de esta mutación, ya que el State es reactivo.
Este proceso será repetido tantas veces como sea necesario por nuestra aplicación. En el siguiente gráfico podemos ver de una forma clara lo expresado anteriormente:



Veamos una posible implementación de vuex en nuestro proyecto.


El primer paso será agregar la librería al proyecto con npm o yarn:


 
	# NPM
	npm install vuex@next --save
	
	# YARN
	yarn add vuex@next --save

Luego debemos crear nuestra store utilizando la librería, en este caso la creamos en un archivo .js llamado "index", pero podría ser cualquier archivo js. Acá vamos a definir nuestro state inicial, las mutations, las actions, los getters y si hiciera falta los modules.




El siguiente paso una vez tenemos nuestro store creado es agregarlo al "createApp()" como si fuera un plugin:




Y así de fácil estamos listos para poder utilizar nuestra store en Vue.js con Vuex. Veamos ahora cómo podemos acceder a la misma desde nuestro componente. Para ello tenemos el siguiente ejemplo implementando el “optionsApi” con las maneras más comunes para acceder al "state":




Notemos que en el primer “h2” del template estamos accediendo al store de manera directa a través de un objeto global.
En el segundo “h2” accedemos a la misma a través de una propiedad computada definida en la línea 13.
Y por último, también podemos acceder al state con el objeto que nos brinda la librería llamado “mapState” esparciendo los atributos del state que necesitemos dentro de nuestras propiedades computadas como se ve en la línea 16 comentada, o como en la línea 17. La diferencia entre una y otra sería que en la segunda podemos renombrar nosotros como queramos a esta propiedad en caso de que lo necesitemos.


Podemos ver también en el siguiente ejemplo, cómo acceder a las mutations desde nuestro componente:



Vemos que tenemos dos botones que están haciendo uso de dos métodos creados en el atributo "methods".
La primer función, “incrementarContador” utiliza el objeto global y realiza un commit, que es una función que espera recibir como primer parámetro el nombre exacto que definimos para nuestro mutations en el store, el cual luego será ejecutado realizando lo que indicamos en el store.
En el segundo método, “incrementarEn5” vemos como se vuelve a hacer un segundo commit pero esta vez, además de pasar el nombre del mutations que queremos realizar, estamos pasando un segundo parámetro que llegará al mismo y, en este caso, incrementara nuestro contador en 5 unidades.


Luego tenemos las actions, que usualmente usaremos cuando queramos hacer una acción asíncrona para luego modificar nuestro state. Un ejemplo de una posible implementación sería la siguiente:




Aquí volvemos a tener dos botones. El primero llama a un método declarado en methods llamado “incrementarRandom” que realiza un dispatch utilizando el objeto global $store y que llama a la actions creada anteriormente en el store llamada con el mismo nombre.
Esta actions es asíncrona, y luego de resolverse la promesa terminará llamando al mutations que corresponda para actualizar el state (ver imagen con la creación del store).
En el segundo caso, lo que hacemos es importarnos el "mapActions" expuesto por la librería Vuex para hacer algo parecido a lo que vimos anteriormente con el "mapState" definiendo un nuevo nombre “incrementarAction”.


Por último, veamos cómo podríamos trabajar con módulos dentro de nuestro store, ya que esta es la manera ideal de trabajar con Vuex y que sea más fácil poder dar un mejor mantenimiento a nuestro código. La idea sería no tener un store donde tengamos un gran estado con todo lo que necesitemos para nuestra aplicación, sino separar nuestro state, getters, mutations y actions en porciones más pequeñas de lógica en distintos módulos y, que todos en conjunto, formen parte de nuestra store.


Para conseguir esto, primero quitamos todos los atributos de la creación de nuestro store y colocamos solo los “modules”. Dentro de esta propiedad podremos tener todos los distintos módulos de nuestra aplicación.




El “contadorStore” es la lógica de nuestra antigua store separada esta vez en otro archivo js donde colocamos el state, getters, actions y mutations que habíamos declarado antes:




Aquí la pequeña diferencia es, que el “state” esta vez es una función implícita que retorna un objeto. Esto debe ser así porque, al tratarse de un módulo, el “state” no es reactivo y Vuex necesita de esta función para que cuando sea agregado al store mediante “modules” pueda trabajarlo de manera reactiva.


Luego restaría hacer unas pequeñas modificaciones a nuestro componente para que todo funcione como se espera, ya que ahora que nos manejamos con un módulo debemos indicar, tanto cuando accedemos de manera directa al store por el objeto global como cuando accedemos por los “maps” que nos expone la libreria, de que modulo estaremos extrayendo el state o las actions o etc.
La referencia ya no será más “this.$store.state” sino que ahora será “this.$store.state.moduloContador”, ya que este el nombre que le definimos al módulo al momento de crearlo, dentro de “modules”.
También, cuando realicemos un commit, un dispatch o un maps, deberemos indicar el módulo al que queremos acceder, pasándole en el primer parámetro, no solo el nombre de la actions o mutations sino también el nombre del mismo, como por ejemplo “moduloContador/incrementar”.




Conclusión


Vuex nos ayuda a lidiar con la gestión de nuestro estado, haciendo más fácil la comunicación entre nuestros componentes y nos da la posibilidad de centralizar el mismo, sabiendo en todo momento dónde está la información actualizada para poder ser accedida. Pero por un costo de mayor estructura y archivos que deben ser mantenidos por nosotros. Es una compensación entre la productividad de corto y largo plazo, y que si bien no es complicado de implementar, nos va a sumar complejidad a la hora de mantener nuestro código.
Probablemente para aplicaciones simples no necesitemos de Vuex, pero para construcciones de SPA de mediana o gran escala nos sea de gran ayuda.

Mandanos tus sugerencias

Ayudanos con ideas para los artículos de este blog a contacto@somospnt.com

¡Seguínos en nuestras redes sociales para enterarte de los últimos posts!