Definindo uma Memória (store
)
Antes de mergulhar dentro dos conceitos fundamentais, nós precisamos saber que uma memória é definida com a utilização de defineStore()
e que ela requer um nome único, passado como primeiro argumento:
import { defineStore } from 'pinia'
// Tu podes nomear o valor de retorno de `defineStore()` para que quiseres,
// mas é melhor utilizar o nome da memória e envolvê-la com `use` e
// `Store` (por exemplo, `useUserStore`, `useCartStore`, `useProductStore`)
// `useStore`, poderia ser qualquer coisa tipo, `userUser`, `useCart` (remover)
// o primeiro argumento é um identificador único da memória na tua aplicação
export const useStore = defineStore('main', {
// outras opções...
})
import { defineStore } from 'pinia'
// Tu podes nomear o valor de retorno de `defineStore()` para que quiseres,
// mas é melhor utilizar o nome da memória e envolvê-la com `use` e
// `Store` (por exemplo, `useUserStore`, `useCartStore`, `useProductStore`)
// `useStore`, poderia ser qualquer coisa tipo, `userUser`, `useCart` (remover)
// o primeiro argumento é um identificador único da memória na tua aplicação
export const useStore = defineStore('main', {
// outras opções...
})
Este nome, também referenciado como id, é necessário e é usado pela Pinia para conectar a memória à ferramenta do programador (devtools, em Inglês). A nomenclatura que a função retornada utiliza... é uma convenção entre os constituíveis (composables, termo em Inglês) para tornar a sua utilização idiomática.
A defineStore()
aceita dois valores distintos para o seu segundo argumento: uma função de Configuração ou um objeto de Opções.
Memórias baseadas em Opções
Semelhante a API de Opções da Vue, nós também podemos passar um Objeto de Opções com as propriedades state
, actions
e getters
.
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
},
})
export const useCounterStore = defineStore('counter', {
state: () => ({ count: 0 }),
getters: {
double: (state) => state.count * 2,
},
actions: {
increment() {
this.count++
},
},
})
Tu podes pensar de state
(estado) como o data
(dados) da memória, e getters
(recuperadores) como as propriedades computed
(computadas) da memória, e actions
(ações) como os methods
(métodos).
As memórias baseadas em opções devem ser intuitivas e simples de serem iniciadas.
Memórias baseadas em Composições
Há também uma outra sintaxe possível para definir as memórias. Semelhante a função setup
da API de Composição da Vue, nós podemos passar uma função que define propriedades reativas e métodos, e que retorna uma objeto com as propriedades e métodos que nós queremos expor.
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const name = ref('Eduardo')
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, name, doubleCount, increment }
})
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const name = ref('Eduardo')
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, name, doubleCount, increment }
})
Nas Memórias baseadas em Composições:
- As
ref()
tornam-se propriedadesstate
- As
computed()
tornam-segetters
- As
function()
tornam-seactions
As memórias baseadas composições trazem muito mais flexibilidade do que Memórias baseadas em Opções visto que podes criar observadores dentro de uma memória e utilizar livremente qualquer constituível (composable, termo em Inglês). No entanto, lembra-te de que a utilização de constituíveis tornará Interpretação no Lado do Servidor (SSR, sigla em Inglês) complexa.
Qual sintaxe eu deveria escolher?
De acordo com o artigo que fala sobre a escolha entre a API de Composição e API de Opções da Vue, escolha aquela com a qual estás mais confortável. Se não estiveres certo de qual, experimente primeiro a Memórias baseadas em Opções.
Utilizando a memória
Nós estamos definindo uma memória porque a memória não será criada até que a useStore()
seja chamada dentro de setup()
:
import { useCounterStore } from '@/stores/counter'
export default {
setup() {
const store = useCounterStore()
return {
// retornar uma instância da memória
// para utilizá-la no modelo de marcação.
store,
}
},
}
import { useCounterStore } from '@/stores/counter'
export default {
setup() {
const store = useCounterStore()
return {
// retornar uma instância da memória
// para utilizá-la no modelo de marcação.
store,
}
},
}
Dica
Se ainda não estiveres a utilizar os componentes setup
, podes continuar a utilizar a Pinia com os mapas auxiliares.
Tu podes definir quantas memórias que quiseres e deves definir cada memória em um ficheiro diferente para obter o melhor da pinia (tal como permitir a separação de código e fazer inferência de TypeScript do teu pacote automaticamente).
Uma vez que a memória é instanciada, podes acessar diretamente qualquer propriedade state
, getters
, e actions
definida na memória. Nós veremos estes em detalhe nas próximas páginas mas a conclusão automática ajudar-te-á.
Nota que a store
é um objeto envolvido com a reactive
, querendo dizer que não é preciso escrever .value
depois dos recuperadores (getters, em Inglês) mas, tal como as props
em setup
, nós não podemos desestruturá-las:
export default defineComponent({
setup() {
const store = useCounterStore()
// ❌ Isto não funcionará porque quebra a reatividade
// é o mesmo que desestruturar a partir de `props`
const { name, doubleCount } = store
name // "Eduardo"
doubleCount // 0
setTimeout(() => {
store.increment()
}, 1000)
return {
// sempre será "eduardo"
name,
// sempre será 0
doubleCount,
// também sempre será 0
doubleNumber: store.doubleCount,
// ✅ este aqui será reativo
doubleValue: computed(() => store.doubleCount),
}
},
})
export default defineComponent({
setup() {
const store = useCounterStore()
// ❌ Isto não funcionará porque quebra a reatividade
// é o mesmo que desestruturar a partir de `props`
const { name, doubleCount } = store
name // "Eduardo"
doubleCount // 0
setTimeout(() => {
store.increment()
}, 1000)
return {
// sempre será "eduardo"
name,
// sempre será 0
doubleCount,
// também sempre será 0
doubleNumber: store.doubleCount,
// ✅ este aqui será reativo
doubleValue: computed(() => store.doubleCount),
}
},
})
Para extrair propriedades da memória enquanto preserva-se a sua reatividade, precisas utilizar a storeToRefs()
. Ela criará referências para todas as propriedades reativas. Isto é útil para quando estiveres apenas utilizando o estado da memória mas não chamando nenhuma ação. Nota que podes desestruturar as ações diretamente da memória visto que elas também estão presas a própria memória:
import { storeToRefs } from 'pinia'
export default defineComponent({
setup() {
const store = useCounterStore()
// `name` e `doubleCount` são referências reativas
// também criará referências
// para as propriedades adicionas pelas extensões
// mas ignorará qualquer ação ou
// propriedade não reativa e ou referenciada
const { name, doubleCount } = storeToRefs(store)
// a ação `increment` já pode ser extraída
const { increment } = store
return {
name,
doubleCount,
increment,
}
},
})
import { storeToRefs } from 'pinia'
export default defineComponent({
setup() {
const store = useCounterStore()
// `name` e `doubleCount` são referências reativas
// também criará referências
// para as propriedades adicionas pelas extensões
// mas ignorará qualquer ação ou
// propriedade não reativa e ou referenciada
const { name, doubleCount } = storeToRefs(store)
// a ação `increment` já pode ser extraída
const { increment } = store
return {
name,
doubleCount,
increment,
}
},
})