The Pokedex Project(12) — Desarrollando nuestro front end. Parte 3. Implementando Vuex en nuestro front end

Chris Lopez
7 min readMar 19, 2021

--

En el episodio pasado vimos como hacer una petición asíncrona, para popular un prop de data y llenara nuestra vista de Pokemones.

Ahora veremos como implementar el manejo de estados entre nuestros componentes con Vuex. Esto nos serviría para poder escalar nuestra aplicación en caso de que creciera o hubiera mas lógica de negocio por parte del front-end.

Vuex es una librería que usa el patrón de diseño de “Manejo de Estado”. Se integra únicamente con Vue. Sirve como un almacén de estado centralizado para los componentes de tu app.

Si quieres saber mas de Vuex: https://vuex.vuejs.org/#what-is-a-state-management-pattern

El modelo en diagrama de como se implementa Vuex en tu app seria como el siguiente:

Sabiendo esto a desarrollar !!!

Modificamos el package.json en la sección de “dependencies” con lo siguiente:

"vuex": "^3.5.1"

Actualizamos nuestras librerías de node:

npm install

Con esto ya deberíamos de tener instalado “Vuex” en nuestro proyecto.

Ahora tenemos que inicializar “Vuex” en nuestro punto de entrada, modificamos app.js:

import Vuex from "vuex"

Vue.use(Vuex)

Inicializamos el Store de Vuex con lo siguiente en app.js:

const store = new Vuex.Store({
state: {
pokemonList: [],
pokemonFilterList: []
},
getters: {
POKEMON_LIST: state => {
return state.pokemonList
},
POKEMON_FILTER_LIST: state => {
return state.pokemonFilterList
},
},
mutations: {
SET_POKEMON_LIST(state, data) {
state.pokemonList = data
},
SET_POKEMON_FILTER_LIST(state, data) {
state.pokemonFilterList = data
},
}
})

Lo integramos en nuestra instancia de Vue:

new Vue({
el: "#app",
delimiters: ['${', '}'],
components: {
'the-nav': TheNav,
'the-search-box': TheSearchBox,
'the-list-data': TheListData,
'the-footer': TheFooter,
},
mounted() {
this.getData()
},
store,

Para llamar algún “state” del store, tenemos que crear una “computed property” en la instancia de Vue; en este caso usaremos “pokemonList” y “pokemonFilterList”:

computed: {
dataList: {
get () {
return this.$store.getters.POKEMON_LIST
},
set (value) {
this.$store.commit('SET_POKEMON_LIST', value)
}
},
filterDataList: {
get () {
return this.$store.getters.POKEMON_FILTER_LIST
},
set (value) {
this.$store.commit('SET_POKEMON_FILTER_LIST', value)
}
},
},

Una vez hecho esto modificaremos nuestro método asíncrono que hace la petición para obtener los pokemones:

async getData() {
let dataResponse = await fetch('/show')
this.dataList = await dataResponse.json()
this.filterDataList = this.dataList
},

Nuestro archivo app.js debería de quedarnos así:

import Vue from "vue"
import Vuex from "vuex"

Vue.use(Vuex)

import TheNav from "./components/TheNav"
import TheSearchBox from "./components/TheSearchBox"
import TheListData from "./components/TheListData"
import TheFooter from "./components/TheFooter"

const store = new Vuex.Store({
state: {
pokemonList: [],
pokemonFilterList: []
},
getters: {
POKEMON_LIST: state => {
return state.pokemonList
},
POKEMON_FILTER_LIST: state => {
return state.pokemonFilterList
},
},
mutations: {
SET_POKEMON_LIST(state, data) {
state.pokemonList = data
},
SET_POKEMON_FILTER_LIST(state, data) {
state.pokemonFilterList = data
},
}
})

new Vue({
el: "#app",
delimiters: ['${', '}'],
components: {
'the-nav': TheNav,
'the-search-box': TheSearchBox,
'the-list-data': TheListData,
'the-footer': TheFooter,
},
mounted() {
this.getData()
},
store,
computed: {
dataList: {
get () {
return this.$store.getters.POKEMON_LIST
},
set (value) {
this.$store.commit('SET_POKEMON_LIST', value)
}
},
filterDataList: {
get () {
return this.$store.getters.POKEMON_FILTER_LIST
},
set (value) {
this.$store.commit('SET_POKEMON_FILTER_LIST', value)
}
},
},
data () {
return {
message: "Hello Pokedex!!!"
}
},
methods: {
async getData() {
let dataResponse = await fetch('/show')
this.dataList = await dataResponse.json()
this.filterDataList = this.dataList
},
}
})

Refactorizando nuestro store.

Hasta el momento en nuestro pokedex parace funcionar todo bien, nada mas que hay un problema. Según el diagrama de Vuex que anteriormente mostramos, toda petición a algún back-end o api se debe hacer por medio del store en “actions” y ahí mutar los state del store. Como esta actualmente se actualiza desde la instancia de Vue. Vamos aplicar estos cambios en nuestro store.

Modificamos el store que se encuentra inicializado en app.js:

actions: {
async getData(context, value) {
let response = await fetch('/show')
let dataResponse = await response.json()

context.commit('SET_POKEMON_LIST', dataResponse)
context.commit('SET_POKEMON_FILTER_LIST', dataResponse)
},
},

Si nos fijamos es la misma función que teníamos en “methods” de la instancia de Vue. A diferencia que aqui mutamos los states cuando se resuelve el fetch de los datos, esto se aplica con la función “commit” del contexto del store.

Ahora llamaremos la “action” correspondiente a “getData” definida en el store; esto lo haremos en el metodo mounted() de la instancia de Vue:

this.$store.dispatch('getData')

Y así obtenemos el miso resultado pero ya nos indica Vue Dev Tools que estamos usando el Vuex y estamos mutando los “states”:

Nuestro archivo app.js debería quedar así:

import Vue from "vue"
import Vuex from "vuex"

Vue.use(Vuex)

import TheNav from "./components/TheNav"
import TheSearchBox from "./components/TheSearchBox"
import TheListData from "./components/TheListData"
import TheFooter from "./components/TheFooter"

const store = new Vuex.Store({
state: {
pokemonList: [],
pokemonFilterList: []
},
getters: {
POKEMON_LIST: state => {
return state.pokemonList
},
POKEMON_FILTER_LIST: state => {
return state.pokemonFilterList
},
},
mutations: {
SET_POKEMON_LIST(state, data) {
state.pokemonList = data
},
SET_POKEMON_FILTER_LIST(state, data) {
state.pokemonFilterList = data
},
},
actions: {
async getData(context, value) {
let response = await fetch('/show')
let dataResponse = await response.json()

context.commit('SET_POKEMON_LIST', dataResponse)
context.commit('SET_POKEMON_FILTER_LIST', dataResponse)
},
},
})

new Vue({
el: "#app",
delimiters: ['${', '}'],
components: {
'the-nav': TheNav,
'the-search-box': TheSearchBox,
'the-list-data': TheListData,
'the-footer': TheFooter,
},
created() {
this.$store.dispatch('getData')
},
store,
computed: {
dataList: {
get () {
return this.$store.getters.POKEMON_LIST
},
set (value) {
this.$store.commit('SET_POKEMON_LIST', value)
}
},
filterDataList: {
get () {
return this.$store.getters.POKEMON_FILTER_LIST
},
set (value) {
this.$store.commit('SET_POKEMON_FILTER_LIST', value)
}
},
},
data () {
return {
message: "Hello Pokedex!!!"
}
},
})

Otra de las refactorizaciones que aplicaremos al código, es la técnica “code splitting” técnica de JS que lo que tienes que hacer es dividir el código en varios archivo para después importarlos; sirve mucho cuando las aplicaciones empiezan a crecer y optimiza la carga de archivos, lo cual lo hace mas rápido.

Como vemos nuestro archivo app.js empieza a crecer aunque en estos momentos no pesa mucho, pero debemos aplicar un poco de “code splitting”.

Primero creamos los directorios y archivos(asumo que se esta en el directorio resources/js)

mkdir store && cd store && touch store.js

Para hacer la prueba del import de store.js, copiamos el código del store en app.js y lo ponemos en store.js de la siguiente manera:

export default new Vuex.Store( {
state: {
pokemonList: [],
pokemonFilterList: []
},
getters: {
POKEMON_LIST: state => {
return state.pokemonList
},
POKEMON_FILTER_LIST: state => {
return state.pokemonFilterList
},
},
mutations: {
SET_POKEMON_LIST(state, data) {
state.pokemonList = data
},
SET_POKEMON_FILTER_LIST(state, data) {
state.pokemonFilterList = data
},
},
actions: {
async getData(context, value) {
let response = await fetch('/show')
let dataResponse = await response.json()

context.commit('SET_POKEMON_LIST', dataResponse)
context.commit('SET_POKEMON_FILTER_LIST', dataResponse)
},
},
})

Agregamos las librerías de Vue y Vuex:

import Vue from "vue"
import Vuex from "vuex"

Vue.use(Vuex)

El archivo store.js deberia de quedar así:

import Vue from "vue"
import Vuex from "vuex"

Vue.use(Vuex)

export default new Vuex.Store( {
state: {
pokemonList: [],
pokemonFilterList: []
},
getters: {
POKEMON_LIST: state => {
return state.pokemonList
},
POKEMON_FILTER_LIST: state => {
return state.pokemonFilterList
},
},
mutations: {
SET_POKEMON_LIST(state, data) {
state.pokemonList = data
},
SET_POKEMON_FILTER_LIST(state, data) {
state.pokemonFilterList = data
},
},
actions: {
async getData(context, value) {
let response = await fetch('/show')
let dataResponse = await response.json()

context.commit('SET_POKEMON_LIST', dataResponse)
context.commit('SET_POKEMON_FILTER_LIST', dataResponse)
},
},
})

En app.js quitamos las lineas duplicadas e importamos el store.js:

import store from "./store/store"

Nos debería de quedar así:

import Vue from "vue"

import store from "./store/store"

import TheNav from "./components/TheNav"
import TheSearchBox from "./components/TheSearchBox"
import TheListData from "./components/TheListData"
import TheFooter from "./components/TheFooter"

new Vue({
el: "#app",
delimiters: ['${', '}'],
components: {
'the-nav': TheNav,
'the-search-box': TheSearchBox,
'the-list-data': TheListData,
'the-footer': TheFooter,
},
created() {
this.$store.dispatch('getData')
},
store,
computed: {
dataList: {
get () {
return this.$store.getters.POKEMON_LIST
},
set (value) {
this.$store.commit('SET_POKEMON_LIST', value)
}
},
filterDataList: {
get () {
return this.$store.getters.POKEMON_FILTER_LIST
},
set (value) {
this.$store.commit('SET_POKEMON_FILTER_LIST', value)
}
},
},
data () {
return {
message: "Hello Pokedex!!!"
}
},
})

Si todo sale bien deberíamos de ver la misma pagina del Pokedex.

Ahora aplicaremos “code splitting” al store, para esto necesitamos crear un archivo, lo llamaremos storeOptions.js(esto en el directorio js/store), y ponemos lo siguiente:

export default {
state: {
pokemonList: [],
pokemonFilterList: []
},
getters: {
POKEMON_LIST: state => {
return state.pokemonList
},
POKEMON_FILTER_LIST: state => {
return state.pokemonFilterList
},
},
mutations: {
SET_POKEMON_LIST(state, data) {
state.pokemonList = data
},
SET_POKEMON_FILTER_LIST(state, data) {
state.pokemonFilterList = data
},
},
actions: {
async getData(context, value) {
let response = await fetch('/show')
let dataResponse = await response.json()

context.commit('SET_POKEMON_LIST', dataResponse)
context.commit('SET_POKEMON_FILTER_LIST', dataResponse)
},
},
}

Borramos el código duplicado en store.js e importamos storeOptions.js:

import Vue from "vue"
import Vuex from "vuex"

Vue.use(Vuex)

import storeOptions from "./storeOptions";

export default new Vuex.Store( storeOptions )

Ahora aplicaremos la misma técnica para storeOptions.js, creamos los archivos state.js, getters.js, mutations.js y actions.js, y ponemos cada parte en los archivos.

Creamos y editamos state.js:

export default {
pokemonList: [],
pokemonFilterList: []
}

Creamos y editamos getters.js:

export default {
POKEMON_LIST: state => {
return state.pokemonList
},
POKEMON_FILTER_LIST: state => {
return state.pokemonFilterList
},
}

Creamos y editamos mutations.js:

export default {
SET_POKEMON_LIST(state, data) {
state.pokemonList = data
},
SET_POKEMON_FILTER_LIST(state, data) {
state.pokemonFilterList = data
},
}

Creamos y editamos actions.js:

export default {
async getData(context, value) {
let response = await fetch('/show')
let dataResponse = await response.json()

context.commit('SET_POKEMON_LIST', dataResponse)
context.commit('SET_POKEMON_FILTER_LIST', dataResponse)
},
}

Editamos storeOptions.js:

import state from "./state";
import getters from "./getters";
import mutations from "./mutations";
import actions from "./actions";

export default { state, getters, mutations, actions, }

Y así terminamos el “code splitting” en el store; podemos aplicar lo mismo para la instancia de Vue. Con esto mejoramos el rendimiento de nuestro Pokedex y va mas rápido.

Pueden consultar el código de esta publicación en este repositorio: https://github.com/krsrk/pokedex-vanilla-php

Si les gusto la publicación denle claps, likes y comentarios.

--

--

Chris Lopez
Chris Lopez

Written by Chris Lopez

Laravel, PHP, Python, Js, Vue Developer

No responses yet