Skip to content

Estado

O estado é, na maioria das vezes, a parte central da nossa memória. As pessoas frequentemente começam definindo o estado que representa a sua aplicação. Na Pinia o estado é definido como uma função que retorna o estado inicial. Isto permite a Pinia funcionar tanto no lado do servidor quanto no lado do cliente:

js
import { defineStore } from 'pinia'  export const useStore = defineStore('storeId', {  // função de flecha recomendada para  // completa inferência de tipo  state: () => {  return {  // todas estas propriedades terão  // seus tipos inferidos automaticamente  count: 0,  name: 'Eduardo',  isAdmin: true,  items: [],  hasChanged: true,  }  }, })

DICA

Se estivermos usando a Vue 2, os dados que criamos na state seguem as mesmas regras aplicadas à data numa instância de Vue, isto é, o objeto de estado deve ser simples e precisamos chamar Vue.set() quando adicionamos novas propriedades à este. Consultar também: Vue#data.

TypeScript

Nós precisamos fazer muito no sentido de tornar o nosso estado compatível com a TypeScript: temos que nos certificar de que a strict, ou no mínimo, a noImplicitThis, estão ativadas e a Pinia inferirá o tipo do nosso estado automaticamente! No entanto, existem alguns casos onde deveríamos dar-lhe uma mãozinha com alguma moldagem:

ts
export const useUserStore = defineStore('storeId', {  state: () => {  return {  // para listas inicialmente vazias  userList: [] as UserInfo[],  // para dados que ainda não foram carregados  user: null as UserInfo | null,  }  }, })  interface UserInfo {  name: string  age: number }

Se preferirmos, podemos definir o estado com uma interface e tipificar o valor do retorno da state():

ts
interface State {  userList: UserInfo[]  user: UserInfo | null }  export const useUserStore = defineStore('user', {  state: (): State => {  return {  userList: [],  user: null,  }  }, })  interface UserInfo {  name: string  age: number }

Acessando o state

Por padrão, podemos ler e escrever diretamente ao estado acessando-o através da instância de store:

js
const store = useStore()  store.counter++

Nota que não podemos adicionar um nova propriedade de estado se não a definimos na state(), esta deve conter o estado inicial, por exemplo: não podemos fazer store.secondCount = 2 se secondCount não estiver definida na state().

Redefinindo o Estado

Nas Memórias de Opções, podemos reiniciar o estado ao seu valor inicial chamando o método $reset() na store:

js
const store = useStore()  store.$reset()

Internamente, este chama a função state() para criar um novo objeto de estado e substitui o estado atual por ele.

Nas Memórias de Composições, precisamos criar o nosso próprio método $reset():

ts
export const useCounterStore = defineStore('counter', () => {  const count = ref(0)   function $reset() {  count.value = 0  }   return { count, $reset } })

Uso com a API de Opções

Para os seguintes exemplos, podemos assumir que a seguinte memória foi criada:

js
// Caminho do Ficheiro de Exemplo: // ./src/stores/counterStore.js  import { defineStore } from 'pinia'  export const useCounterStore = defineStore('counter', {  state: () => ({  count: 0,  }), })

Se não estivermos usando a API de Composição, e estivermos usando computed, methods, ..., podemos usar a auxiliar mapState() para mapear as propriedades do estado como propriedades computadas de apenas leitura:

js
import { mapState } from 'pinia' import { useCounterStore } from '../stores/counter'  export default {  computed: {  // dá acesso ao `this.count` dentro do componente  // mesmo que ler de `store.count`  ...mapState(useCounterStore, ['count'])  // mesmo que o de acima exceto que  // a regista como `this.myOwnName`  ...mapState(useCounterStore, {  myOwnName: 'count',  // também podemos escrever uma função  // que recebe a acesso à memória  double: store => store.count * 2,  // pode ter acesso ao `this` mas  // não será tipificada corretamente...  magicValue(store) {  return store.someGetter + this.count + this.double  },  }),  }, }

Estado Modificável

Se quisermos ser capazes de escrever às estas propriedades de estado (por exemplo, se tivermos um formulário). podemos usar a mapWritableState(). Nota que não podemos passar uma função da mesma maneira que a mapState():

js
import { mapWritableState } from 'pinia' import { useCounterStore } from '../stores/counter'  export default {  computed: {  // dá acesso ao `this.count` dentro do componente  // e permite defini-lo `this.count+++`  // o mesmo que ler de `store.count`  ...mapWritableState(useCounterStore, ['count'])  // o mesmo que o de cima exceto que  // a regista como `this.myOwnName`  ...mapWritableState(useCounterStore, {  myOwnName: 'count',  }),  }, }

DICA

Nós não precisamos da mapWritableState() para coleções como vetores a menos que estejamos substituindo o vetor inteiro com cartItems = [], a mapState() ainda permite-nos chamar os métodos sobre as nossas coleções.

Alterando o Estado

Para além de alterarmos diretamente a memória com store.count++, também podemos chamar o método $patch. Este permite-nos aplicar várias mudanças ao mesmo tempo com um objeto state parcial:

js
store.$patch({  count: store.count + 1,  age: 120,  name: 'DIO', })

No entanto, algumas mutações são muito difíceis ou dispendiosas de aplicar-se com esta sintaxe: qualquer modificação da coleção (por exemplo, empurrar, remover, juntar um elemento dum vetor) exige que criemos uma nova coleção. Por causa disto, o método $patch também aceita uma função para agrupar este tipo de mutações que são difíceis de aplicar com um objeto de remendo:

js
store.$patch((state) => {  state.items.push({ name: 'shoes', quantity: 1 })  state.hasChanged = true })

A principal diferença aqui é que $patch() permite-nos agrupar várias mudanças dentro duma única entrada na ferramenta de programação do navegador. Nota que ambas, mudanças diretas ao state e patch aparecem na ferramenta de programação do navegador e podem ser viajadas no tempo (ainda não na Vue 3).

Substituindo o state

Nós não podemos substituir exatamente o estado duma memória porque isto quebraria a reatividade. No entanto, podemos remendá-lo:

js
// esta de fato não substitui o `$state` store.$state = { count: 24 } // esta chama internamente o `$patch()`: store.$patch({ count: 24 })

Nós também podemos definir o estado inicial da nossa aplicação inteira mudando a state da instância de pinia. Isto é usado durante a Interpretação do Lado do Servidor para hidratação:

js
pinia.state.value = {}

Subscrevendo ao Estado

Nós podemos observar o estado e suas mudanças através do método $subscribe() duma memória, semelhante ao método subscribe da Vuex. A vantagem de usar $subscribe() em vez da watch() normal é que as subscrições acionarão apenas uma vez após os remendos (por exemplo, quando usamos a versão de função acima):

js
cartStore.$subscribe((mutation, state) => {  // import { MutationType } from 'pinia'  mutation.type // 'direct' | 'patch object' | 'patch function'  // o mesmo que `cartStore.$id`  mutation.storeId // 'cart'  // disponível apenas com `mutation.type` === 'patch object'  mutation.payload // objeto de remendo passado para `cartStore.$patch()`   // persistir o estado inteiro no armazenamento local sempre que mudar  localStorage.setItem('cart', JSON.stringify(state)) })

Por padrão, as subscrições de estado estão vinculadas ao componente onde são adicionadas (se a memória estiver dentro duma setup() do componente). Querendo dizer que, serão removidas automaticamente quando o componente for desmontado. Se também quisermos preservá-las depois do componente ser desmontado, passamos { detached: true } como segundo argumento para separar a subscrição de estado do componente atual:

vue
<script setup> const someStore = useSomeStore()  // esta subscrição será preservada // mesmo depois do componente ser desmontado someStore.$subscribe(callback, { detached: true }) </script>

DICA

Nós podemos observar o estado inteiro sobre a instância de pinia com uma única watch():

js
watch(  pinia.state,  (state) => {  // persistir todo o estado no armazenamento local  // sempre que for alterado  localStorage.setItem('piniaState', JSON.stringify(state))  },  { deep: true } )
Turn AI into a coding partner
Claim 40% off for AIDD Masterclass.
Get discount