En este módulo, exploraremos patrones avanzados de Vuex que te permitirán gestionar el estado de tu aplicación de manera más eficiente y escalable. Estos patrones son especialmente útiles en aplicaciones grandes y complejas.

Contenido

Normalización del Estado

¿Qué es la Normalización del Estado?

La normalización del estado implica estructurar el estado de tu aplicación de manera que se minimicen las redundancias y se facilite el acceso y la manipulación de los datos. Esto es especialmente útil cuando trabajas con datos anidados o relaciones entre entidades.

Ejemplo

Supongamos que tienes una lista de artículos y cada artículo tiene un autor. En lugar de almacenar los datos de esta manera:

state: {
  articles: [
    { id: 1, title: 'Artículo 1', author: { id: 1, name: 'Autor 1' } },
    { id: 2, title: 'Artículo 2', author: { id: 2, name: 'Autor 2' } },
  ]
}

Podrías normalizar el estado así:

state: {
  articles: [1, 2],
  authors: [1, 2],
  entities: {
    articles: {
      1: { id: 1, title: 'Artículo 1', author: 1 },
      2: { id: 2, title: 'Artículo 2', author: 2 },
    },
    authors: {
      1: { id: 1, name: 'Autor 1' },
      2: { id: 2, name: 'Autor 2' },
    }
  }
}

Ventajas

  • Reducción de Redundancia: Los datos no se duplican, lo que facilita su actualización.
  • Acceso Eficiente: Es más fácil acceder y manipular datos específicos.

Uso de Selectores

¿Qué son los Selectores?

Los selectores son funciones que permiten extraer y transformar datos del estado de Vuex de manera eficiente. Son útiles para evitar lógica compleja en los componentes.

Ejemplo

// store/selectors.js
export const getArticleById = (state) => (id) => {
  return state.entities.articles[id];
};

export const getAuthorById = (state) => (id) => {
  return state.entities.authors[id];
};

Uso en Componentes

import { mapState } from 'vuex';
import { getArticleById, getAuthorById } from './store/selectors';

export default {
  computed: {
    ...mapState({
      article: (state) => getArticleById(state)(1),
      author: (state) => getAuthorById(state)(1),
    })
  }
};

Ventajas

  • Reutilización de Lógica: Los selectores pueden ser reutilizados en múltiples componentes.
  • Código Más Limpio: Mantiene la lógica de acceso al estado fuera de los componentes.

Acciones Asíncronas Complejas

Manejo de Acciones Asíncronas

En aplicaciones complejas, es común tener acciones asíncronas que dependen de múltiples llamadas a APIs o que necesitan manejar errores de manera robusta.

Ejemplo

// store/actions.js
export const fetchArticleAndAuthor = async ({ commit }, articleId) => {
  try {
    const articleResponse = await fetch(`/api/articles/${articleId}`);
    const article = await articleResponse.json();
    commit('setArticle', article);

    const authorResponse = await fetch(`/api/authors/${article.author}`);
    const author = await authorResponse.json();
    commit('setAuthor', author);
  } catch (error) {
    console.error('Error fetching article or author:', error);
  }
};

Ventajas

  • Manejo de Errores: Facilita el manejo de errores en un solo lugar.
  • Encadenamiento de Acciones: Permite encadenar múltiples acciones asíncronas de manera clara.

Patrón de Repositorio

¿Qué es el Patrón de Repositorio?

El patrón de repositorio abstrae la lógica de acceso a datos, permitiendo que las acciones de Vuex interactúen con una capa de datos en lugar de directamente con APIs o bases de datos.

Ejemplo

// repository/articleRepository.js
export const fetchArticle = async (id) => {
  const response = await fetch(`/api/articles/${id}`);
  return response.json();
};

// store/actions.js
import { fetchArticle } from '../repository/articleRepository';

export const fetchArticleById = async ({ commit }, id) => {
  const article = await fetchArticle(id);
  commit('setArticle', article);
};

Ventajas

  • Separación de Preocupaciones: Mantiene la lógica de acceso a datos separada de la lógica de estado.
  • Reutilización: Los métodos del repositorio pueden ser reutilizados en diferentes partes de la aplicación.

Modularización Avanzada

¿Qué es la Modularización Avanzada?

La modularización avanzada implica dividir el estado de Vuex en módulos más pequeños y manejables, cada uno con su propio estado, mutaciones, acciones y getters.

Ejemplo

// store/modules/articles.js
const state = {
  all: []
};

const mutations = {
  setArticles(state, articles) {
    state.all = articles;
  }
};

const actions = {
  async fetchArticles({ commit }) {
    const response = await fetch('/api/articles');
    const articles = await response.json();
    commit('setArticles', articles);
  }
};

export default {
  namespaced: true,
  state,
  mutations,
  actions
};

// store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
import articles from './modules/articles';

Vue.use(Vuex);

export default new Vuex.Store({
  modules: {
    articles
  }
});

Ventajas

  • Escalabilidad: Facilita la gestión del estado en aplicaciones grandes.
  • Encapsulamiento: Cada módulo puede ser desarrollado y probado de manera independiente.

Conclusión

En este módulo, hemos explorado varios patrones avanzados de Vuex que te ayudarán a gestionar el estado de tu aplicación de manera más eficiente y escalable. La normalización del estado, el uso de selectores, las acciones asíncronas complejas, el patrón de repositorio y la modularización avanzada son técnicas que pueden mejorar significativamente la estructura y mantenibilidad de tu código.

Ejercicio Práctico

Objetivo: Implementar un patrón de repositorio y usarlo en una acción de Vuex.

  1. Crear un Repositorio:

    • Crea un archivo repository/userRepository.js.
    • Define una función fetchUser que haga una llamada a /api/users/:id.
  2. Modificar las Acciones de Vuex:

    • Crea una acción fetchUserById en tu módulo de Vuex que use fetchUser del repositorio.
  3. Probar la Acción:

    • Llama a fetchUserById desde un componente y muestra los datos del usuario en la interfaz.

Solución

// repository/userRepository.js
export const fetchUser = async (id) => {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
};

// store/modules/users.js
import { fetchUser } from '../../repository/userRepository';

const state = {
  user: null
};

const mutations = {
  setUser(state, user) {
    state.user = user;
  }
};

const actions = {
  async fetchUserById({ commit }, id) {
    const user = await fetchUser(id);
    commit('setUser', user);
  }
};

export default {
  namespaced: true,
  state,
  mutations,
  actions
};

// Component.vue
<template>
  <div>
    <p>User: {{ user.name }}</p>
  </div>
</template>

<script>
import { mapState } from 'vuex';

export default {
  computed: {
    ...mapState('users', ['user'])
  },
  created() {
    this.$store.dispatch('users/fetchUserById', 1);
  }
};
</script>

Con esto, has implementado un patrón de repositorio y lo has utilizado en una acción de Vuex, mejorando la estructura y mantenibilidad de tu aplicación.

Curso de Vue.js

Módulo 1: Introducción a Vue.js

Módulo 2: Conceptos Básicos de Vue.js

Módulo 3: Componentes de Vue.js

Módulo 4: Vue Router

Módulo 5: Gestión de Estado con Vuex

Módulo 6: Directivas de Vue.js

Módulo 7: Plugins de Vue.js

Módulo 8: Pruebas en Vue.js

Módulo 9: Conceptos Avanzados de Vue.js

Módulo 10: Construcción y Despliegue de Aplicaciones Vue.js

Módulo 11: Proyectos Reales con Vue.js

© Copyright 2024. Todos los derechos reservados