Vuex con TypeScript
Tags: vue, vuex, typescript
Published: 2020-05-22 22:08:23

Hola!, primero que nada darte las gracias por entrar al post, paso a presentarme de forma breve.

Mi nombre es Rodrigo y trabajo principalmente en Backend con Go y NodeJS. 
Mi experiencia con todo el ecosistema de Frontend es relativamente nueva, pero está siendo más que satisfactoria experiencia :)

Vamos a comenzar a hablar del post.

#Vuex con Typescript

###Que es éste post y que no es.
* Es un explicativo de implementación funcional y fácil de entender de Vuex usando TypeScript.
* No es un paso a paso de un proyecto desde 0 con Vue, Vuex y TypeScript.

Tuve que hacer un proyecto fullstack en la facultad y elegí como framework de frontend a Vue ya que me pareció tener una curva de aprendizaje relativamente sencilla y viendo ejemplos creía poder llegar a algo lindo.
Probé hacer proyectos chicos usando Vue con JS plano y todo salió perfecto.

Ahora, en ocasiones me siento más cómodo usando TypeScript y resultó ser que Vue acepta usar TypeScript en lugar de JS, ese fue mi pie para darme el OK a usar Ts en mi proyecto.

A medida que fui haciendo, fui descubriendo que las cosas se hacían de distinta manera al regular de JS con Vue, pero todo salía sin inconveniente, hasta que... tuve que empezar a implementar Vuex.

Este fue el punto que más me costó de todo el proyecto, por todo los lugares donde buscará referencias, o se implementaba de maneras distintas o todos los artículos estaban incompletos.

Con esto último que acabo de decir, espero que quede claro la consigna del post, **un ejemplo FUNCIONAL y que abarque todos los aspectos de la implementación de Vuex con TypeScript**.

### Empecemos!!!

*Your Wall*

Es el nombre que le di al proyecto, es algo super sencillo en donde:
1. Se completa una descripción.
2. Se carga una imagen. 
3. Se envia la imagen y la descripción a un backend.
4. Luego el backend nos devuelve una lista de todas las imágenes que se han enviado.

*Sencillo, ¿cierto?*.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/v8nckjcq48p7gjlqxqvu.png)

Lo que yo necesitaba era que, cada vez que mi aplicación cargue, traiga todos los posts del backend y cuando yo envíe uno nuevo, me actualice la lista ya existente en mi aplicativo.

## La implementación que SÍ funciona

Para la implementación voy a mostrar ciertas líneas de código para que tengamos presente su funcionamiento y la relación entre Vuex y los componentes.

Vamos a mostrar 2 componentes los cuales son: 
1. AddPost.vue 
2. PostList.vue

Para vuex solo tenemos 1 store el cual es: 
1. Post.ts

Estructura:
```cmd
your-wall
├── store
    ├── modules
├── components
```

Vamos a instalar una libreria llamada ['vuex-module-decorators'](https://github.com/championswimmer/vuex-module-decorators).

Dentro de la carpeta store vamos a crear el archivo index.ts, con el siguiente código:
```javascript
import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {},
  actions: {},
  mutations: {},
});

export default store;
```

Lo vamos a utilizar para inicializar el store de vuex.

Dentro de store, creamos la carpeta modules con el archivo Post.ts dentro.

Vamos a importar las dependencias de 'vuex-module-decorators'.

```javascript
import { VuexModule, Module, Mutation, Action, config, getModule} from 'vuex-module-decorators';
```
Luego importo la interfaz del Post y el store que inicializamos más arriba!
```javascript
import { Post } from '../models';
import store from '@/store';
```
Definimos el Módulo:
```javascript
config.rawError = true;
@Module({
  name: 'posts',
  namespaced: true,
  dynamic: true,
  store,
})
```
La primer línea del config.rawError es por si ocurre algún error con la librería, nos devuelva un error-dev-newbie-friendly y no una excepción de la librería.

Luego usando la notación @ definimos el nombre del módulo, le inyectamos el store y para esta forma de utilizar el módulo, tenemos que indicarle que es dinámico, ya que no lo estamos definiendo en el Store per se.

Teniendo esto hecho, vamos a proceder a definir la clase y los métodos del módulo!

La definición de la clase es la siguiente:
```javascript
class PostStore extends VuexModule { }
```
Vamos a tener 1 estado, 1 get, 2 mutaciones y 2 acciones.

El estado es un array de la interfaz que importamos:
```javascript
posts: Post[] = [];
```

El get nos va a retornar el estado:
```javascript
  get Posts() {
    return this.posts;
  }
```

Ahora, vamos a declarar las 2 mutaciones que luego van a ser triggereadas por las acciones.

Esta mutación recibe por parámetro un Post y lo agrega en la primer posición del array posts.
```javascript
 @Mutation
  addPost(post: Post) {    
    this.posts.unshift(post);
  }
```

Esta mutación recibe por parámetro un array de Posts y los agrega al estado posts.
```javascript
  @Mutation
  setPosts(postParam: Post[]){
    this.posts = postParam;
  }
```
Ahora vamos con las acciones, vamos a tener 2 también!

Cuando definimos una acción, le tenemos qué indicar a que mutación va a afectar, en este caso va a mutar a 'addPost' que definimos arriba.

Es simplemente una función que recibe por parámetro un formdata que cuenta con 2 atributos, lo postea al backend y lo retorna a la mutación 'addPosts'.
```javascript
  @Action({commit: 'addPost'})
  async addPostAct(data: FormData) {

    const requestOptions = {
      method: 'POST',
      body: data,
    }
    const response = await fetch('http://localhost:8888/api/post', requestOptions);
    if (response.ok) {
      const res: Post = await response.json();
      return res;
    }
  }
```
Esta acción busca los posts al backend y muta a 'setPosts' con la data que nos devolvió el backend.
```javascript
  @Action({commit: 'setPosts'})
  async getPosts() {
    const response = await fetch("http://localhost:8888/api/post");    
    if (response.ok) {
      const res: Post[] = await response.json();
      return res;
    }
  }  
```
Y por finalizar, debajo de la definición de la clase vamos a exportar al módulo :D
```javascript
export default getModule(PostStore);
```
###BIEN, ya tenemos listo Vuex, vamos a implementarlo en los componentes!!

Vamos a empezar con el componente PostList.vue.

Importamos el módulo que acabamos de configurar:
```javascript
import posts from '@/store/modules/Post';
```

En nuestra definición de clase, vamos a agregar que nos traiga todos los posts del backend y usaremos el estado que declaramos!
```javascript
  get posts() {
    return posts.posts
  }
```

Y usando el método created() del life-cycle de Vue, llamamos al store para traer los posts del backend!
```javascript
  async created() {
    await posts.getPosts()
  }
```

##SUPER FÁCIL

Cuando usamos **'posts'**, estamos haciendo referencia al módulo que importamos del store.

Un ejemplo del template de ese componente puede ser el siguiente:
```html
<template>
  <div>
    <div class="container">
      <div v-bind:key="post.id" v-for="post in posts">
        <PostItem v-bind:post="post"/>
      </div>
    </div>
  </div>
</template>
```

Bien, éste componente ya está listo, ya está llamando al backend para que nos traiga los posts y cuando alteramos el estado, lo vamos a tener actualizado siempre!

Vamos al otro componente, AddPost.vue.

Mismo que el ejemplo anterior, importamos el módulo:

```javascript
import posts from '@/store/modules/Post';
```

En este componente lo que vamos a utilizar es la acción de crear un nuevo post, que se vería algo cómo:
```javascript
 async addNewPost() {
     const fd = new FormData();
     fd.append('image', this.selectedFile, this.fileName);
     fd.append('description', this.description);
     await posts.addPostAct(fd);
  }
```

###Y ya está, eso es todo. 

El ejemplo completo estará subido en mi GitHub junto con el backend que use.

###[Repositorio Front](https://github.com/rodrigoherera/your-wall-vuex-typescript)


###[Repositorio Back](https://github.com/rodrigoherera/your-wall-express-typescript)

Espero que les haya servido este ejemplo, yo verdaderamente estuve bastante horas leyendo, probando, re-leyendo, borrando, agregando y terminaba con un spaguetti code que era terrible. 

Espero que tengan un excelente día y nos vemos la próxima!!