Desenvolvimento Web II

Notas de aula do prof. Eduardo da disciplina de Desenvolvimento Web II (curso técnico)

View on GitHub

Gerenciamento de estados

Nas aplicações web, é comum que tenhamos que lidar com estados. Por exemplo, em uma aplicação de cadastro de clientes, podemos ter um estado que indica se o cliente está sendo editado ou não. Em uma aplicação de carrinho de compras, podemos ter um estado que indica se o carrinho está vazio ou não. Em uma aplicação de listagem de filmes, podemos ter um estado que indica se os filmes estão sendo carregados ou não, ou mesmo armazenar os filmes ou gêneros que foram carregados.

No VueJS, podemos usar a API de reatividade para gerenciar estados. Contudo, essa abordagem pode se tornar complexa conforme a aplicação cresce. Para facilitar o gerenciamento de estados, podemos usar uma biblioteca chamada Pinia, como já estudamos anteriormente.

Instalação

Para instalar o Pinia, vamos abrir o terminal e executar o seguinte comando:

npm install pinia

Configuração da aplicação para suportar o Pinia

Vamos editar o arquivo src/main.js e adicionar o seguinte código:

import 'vue-loading-overlay/dist/css/index.css';
import './assets/main.css';

import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import router from './router';
const app = createApp(App);
app.use(router);
app.use(createPinia());

app.mount('#app');

Note que, comparado com o arquivo src/main.js anterior, adicionamos apenas a importação do createPinia e a chamada do método app.use(createPinia()). O método createPinia cria uma instância do Pinia e a torna disponível para todos os componentes da aplicação.

Criando uma store

Podemos dividir o processo de criação de uma store em três etapas:

  1. Criar a store usando o método defineStore
  2. Criar as variáveis reativas e funções que alteram essas variáveis
  3. Retornar as variáveis e funções que serão usadas pelos componentes

Para criar uma store, vamos criar um arquivo chamado src/stores/genre.js e adicionar o seguinte código:

import { reactive, computed } from 'vue';
import { defineStore } from 'pinia';
import api from '@/plugins/axios';

export const useGenreStore = defineStore('genre', () => {
  const state = reactive({
    genres: [],
  });

  const genres = computed(() => state.genres);
  const getGenreName = (id) =>
    state.genres.find((genre) => genre.id === id).name;

  const getAllGenres = async (type) => {
    const response = await api.get(`genre/${type}/list?language=pt-BR`);
    state.genres = response.data.genres;
  };

  return { genres, getAllGenres, getGenreName };
});

Note que, para criar a store, usamos o método defineStore que recebe dois parâmetros: o nome da store e uma função que retorna um objeto com as variáveis reativas e funções que alteram essas variáveis.

No nosso exemplo, criamos uma store chamada genre que possui uma variável reativa chamada genres e três funções: getAllGenres, getGenreName e getGenreName. A função getAllGenres faz uma requisição HTTP para a API do TMDB e armazena os gêneros na variável reativa genres. Esta função recebe um parâmetro chamado type que indica se a listagem de gêneros será feita com base nos filmes ou nas séries. A função getGenreName recebe o id de um gênero e retorna o nome desse gênero.

Note também que a função defineStore retorna um objeto com a propriedade computada genres e as funções getAllGenres e getGenreName. No exemplo não retornamos o objeto reativo state porque não queremos que ele seja acessado diretamente pelos componentes. Para acessar cada um dos elementos do objeto state, vamos usar os getters genres e getGenreName. Da mesma forma, para alterar o estado da aplicação, vamos usar a action getAllGenres.

Por fim, exportamos a store usando o método useGenreStore. Esse método é necessário para que o Pinia possa criar uma instância da store e torná-la disponível para os componentes da aplicação.

Usando a store

Para usar a store, vamos editar o arquivo src/views/MoviesView.vue. Inicialmente vamos editar o bloco script e adicionar o seguinte código:

import { useGenreStore } from '@/stores/genre';

const genreStore = useGenreStore();

Note que importamos a store usando o método useGenreStore que criamos anteriormente. No mesmo bloco script vamos alterar o método onMounted para que ele chame a função getAllGenres da store:

onMounted(async () => {
  isLoading.value = true;
  await genreStore.getAllGenres('movie');
  isLoading.value = false;
});

No exemplo, ao invocar a função getAllGenres, passamos o parâmetro 'movie' para que a listagem de gêneros seja feita com base nos filmes.

Por fim, vamos alterar o bloco template para que ele use a store:

<li
  v-for="genre in genreStore.genres"
  :key="genre.id"
  @click="listMovies(genre.id)"
  class="genre-item"
>
   {{ genre.name }} 
</li>

No exemplo, alteramos apenas a listagem de gêneros, visto que foi o único elemento que usamos da store, e esta é a única store que criamos até o momento. Note que, para acessar a variável reativa genres da store, usamos o getter genres.

Ainda, vamos fazer mais uma alteração no bloco template, para buscar o nome do gênero com base no id do gênero:


<span
  v-for="genre_id in movie.genre_ids"
  :key="genre_id"
  @click="listMovies(genre_id)"
>
  {{ genreStore.getGenreName(genre_id) }}
</span>

Nesse exemplo, usamos a função getGenreName para buscar o nome do gênero com base no id do gênero. Essa função recebe o id do gênero e retorna o nome do gênero e está implementada na store genre.

Exercícios

  1. Fazer as alterações necessárias em TvView.vue para que a listagem de gêneros seja feita usando a store genre. Note que ao chamar a função getAllGenres da store genre, é necessário passar o parâmetro 'tv' para que a listagem de gêneros seja feita com base nos filmes.

< TMDB: Mostrando a tag gêneros TMDB: Enfatizando o gênero atual >