Gerenciando o estado global do Vue de forma amigável: surge o Pinia
Nas aplicações front-end, um dos principais problemas que deve ser resolvido é em relação a como componentes de diferentes hierarquias conversam entre si. A principal solução proposta para o framework Vue.js é o Vuex, uma biblioteca construída a partir da arquitetura Flux que consiste em um local centralizado no qual todos componentes podem acessar e trocar informações. Essa central chama-se Store e é o conceito chave para o gerenciamento de estado global de um App.
Apesar do Vuex funcionar perfeitamente, membros do Vue Core Team concordam que ele tende a ser um pouco complexo, então, em novembro de 2019, Eduardo San Martin Morote fez o primeiro commit de uma biblioteca que viria a ser uma alternativa mais simples, leve e de fácil entendimento: o Pinia. Parafraseando Evan You em seu Twitter, criador do Vue.js, o Pinia pode ser considerado o Vuex 5. De fato, nesse momento de transição para o Vue 3 como opção padrão, o Pinia entra como um gerenciador de estado global sugerido pelas Comand Line Interfaces (CLI), como pode ser visto na figura abaixo:
Neste artigo, farei uma breve comparação do Pinia com o Vuex, mostrando suas novidades, instalação e sintaxe, bem como seu uso prático no Vue 3 com Composition API.
Principais Features
Do Vuex para o Pinia, houve a adição de algumas novidades que facilitaram sua sintaxe e utilização:
- Módulos & Módulos: no Vuex, até então, era necessário existir uma Store central que chamasse as Stores Modules para o funcionamento delas. No Pinia essa Store é inexistente, tendo apenas os módulos totalmente independentes e sendo chamados nos componentes sempre que necessários.
- Sem Mutations: para realizar as mudanças de estado no Vuex, era necessário chamar as Mutations, por meio do “commit”. Por serem consideradas verbosas, foram retiradas no Pinia e toda e qualquer mudança de estado é feita apenas por Actions.
- Pronto para o Debug: a biblioteca oferece acesso à extensão Vue Dev Tools de forma automática e também modular.
- Performático: o Pinia se destaca em questão de performance, pois seu tamanho final fica entorno de 1KB.
- Suporte ao Typescript: adicionar Typescript ao Pinia é relativamente mais simples do que ao Vuex.
- Novos operadores: oferece dois novos operadores que facilitam a codificação de um módulo: $patch e $reset. O primeiro possibilita a atualização de vários States simultaneamente, enquanto o segundo reseta o módulo ao seu valor default, retirando a necessidade de fazer um método para isso como no Vuex.
- State e Getters: no Pinia, State e Getters funcionam da mesma forma por baixo dos panos. Isso possibilita que Getters chamem outros Getters, além de que, caso se opte por utilizar Options API (a mesma sintaxe de componentes do Vue 2) não haverá a presença de mapGetters, apenas mapState, onde ele chamará tanto State quanto Getters.
Instalação
Para começarmos a utilizar a biblioteca, precisamos instalá-la. Para isso, pode-se utilizar yarn ou npm. Vale lembrar que o Pinia está disponível para Vue 2 e 3.
yarn add pinia@next
ou
npm install pinia@next
Após a instalação, damos início ao seu Setup inicial. No arquivo “main.js” importamos o método “createPinia” e o chamamos no “app.use”. Pronto, sua aplicação agora está apta a utilizar o Pinia!
Sintaxe
A principal diferença de sintaxe do Vuex para o Pinia é a remoção das Mutations como anteriormente citado, o restante permanece com seus conceitos similares.
Para organizarmos nosso primeiro módulo, basta criar um arquivo com extensão “.js” em “src/stores”. Nesse exemplo utilizaremos o “counter.js”. Após criarmos o módulo, damos início à sua configuração. Importamos o método “defineStore” do Pinia e fazemos um “export const” dele, utilizando o prefixo “use” e o sufixo “Store” para boas práticas. Nesse caso, ficou “useCounterStore”. Agora precisamos passar quatro parâmetros, são eles:
Id: funciona como um identificador único da Store, ele quem irá identificar o módulo no Vue Dev Tools.
State: são as variáveis da nossa Store. Funciona exatamente da mesma forma que o Data dentro de um componente Vue, onde escrevemos uma função que retorna um objeto com todas as States desejadas. Nesse caso, utilizei um “counter” com valor inicial em 0 e um “name” com valor inicial “Pinia”.
Getters: como citado anteriormente, os Getters funcionam da mesma forma que State. Seu intuito, porém, é serem funções que utilizam da state para retornar valores dela modificados, recebendo a própria state como parâmetro onde é possível acessar States e outros Getters. No exemplo abaixo, temos o “doubleCount” que retorna a State “counter” multiplicado por 2 e “quadrupleCount” que retorna o primeiro Getter também multiplicado por 2.
Actions: por fim, temos as Actions, que tem seu funcionamento igual aos “methods” dentro de um componente Vue. A principal função delas são modificar a State, podendo fazer requisições assíncronas ou simplesmente fazer a troca dos valores diretamente. É interessante ressaltar que, Actions podem ser chamadas em outros módulos da sua Store, desde que faça sentido para o projeto. Além disso, por serem basicamente funções, quando chamadas em um componente, não é necessário a utilização do operador “dispatch”, como no Vuex.
Ao final, teremos algo como:
Em caso de uso do Typescript, podemos criar uma interface que será chamada apenas na parte de State. Nesse exemplo, criei a “ICounter”, que contém todos meus states, além dos getters de forma opcional pois eles não são chamados na primeira parte, mas são importantes de serem adicionados pois torna a tipagem do parâmetro “state” nos getters automatizada, como vemos no código abaixo:
Pinia na Composition API
Terminado o Setup de um módulo, agora podemos utilizá-lo em um componente real. O primeiro passo é, dentro do script, importar o módulo e atribuí-lo a uma constante. Feito isso, na teoria poderíamos desestruturar nossos States e Getters, porém surge um problema: a desestruturação acaba por perder a referência do objeto real. Isso implica que, ao ser alterado um valor, seu resultado não será reativo em tela.
Para resolvermos, surge o operador “storeToRefs”, importado do próprio Pinia. Basta chamá-lo passando o objeto “store” como parâmetro. Refatorando nosso código, ficará assim:
Com nossos Getters e States importados, agora precisamos de seus modificadores. As Actions não podem ser desestruturadas pelo “storeToRefs”, então basta fazer a desestruturação diretamente da store.
Seu componente agora está apto a utilizar o módulo recém criado do Pinia. Vale lembrar que podem ser chamados quantos módulos forem necessários, fazendo a atribuição correta em diferentes objetos e sempre cuidando a questão da referência para não perder a reatividade do seu projeto.
Conclusão
De maneira geral, podemos concluir que o Pinia se tornou uma excelente opção para o gerenciamento de estado global no Vue.js. A biblioteca oferece soluções simples, diretas e de baixa curva de aprendizado, tornando o desenvolvimento mais ágil e limpo. Juntamente a isso, podemos destacar o valor que o Pinia recebeu quando o criador do Framework Javascript ressalta que é uma evolução quase que direta do Vuex, sendo apenas questão de nomenclatura.
Por outro lado, o Vuex não necessariamente entra em desuso visto que sua comunidade continua muito ativa. Atualmente, no momento da escrita deste artigo, o Vuex conta com 27.4k de estrelas no Github, contra 6.1k do Pinia, além de quase 1,8 milhão de downloads semanais no NPM para 99 mil de seu sucessor. Ao mesmo tempo, vale lembrar que com a recente oficialização do Vue 3 + Pinia, esses números devem crescer em favor à essa biblioteca nos próximos meses.