Stores

A Store is a mechanism for maintaining application state in a way that is globally accessible to all components. We use them as intermediary layers to issue API calls to relevant services and cache the results for reuse. Though the thought may occur to use a plain JavaScript object to achieve state management, a Vue Store differs in two distinct ways:

  • Properties are reactive. Changing Store values will automatically propagate to the component-level bindings which use them.
  • Stores enforce a strong process-control for mutating values. Every change within a store is managed through defined methods, ensuring state manipulation is controlled and trackable (using the Vue DevTools).

An application can have as many Stores as needed to logically group state concerns. The Frontier Vue CLI creates strongly typed Stores, enabling full Intellisense and compiler support when writing code against them.

Data within a Store can be managed using three intrinsic functionalities of every Store:

  • Getters: Retrieve a value within the store.
  • Actions: Arbitrary, asynchronous functions that can perform business logic and update the state.
  • State: An object that holds the reactive properties of the store.

Note: We replaced Vuex with Pinia during our migration from Vue 2 to Vue 3 due to the fact that Pinia provides essentially the same API access as Vuex with much less boilerplate and a functional approach.

Technical

A Store in Pinia consists of a standard JavaScript/TypeScript object, with Pinia providing the special functionality:

  • The defineStore function is used to create a store. The Frontier Vue CLI preconfigures it with the necessary options whenever you create a new store.
  • Actions: Methods defined within the store that perform business logic and can update the state. Actions can be asynchronous.
  • Getters: Functions defined within the store that return computed values based on the store's state.
  • State: An object that holds the reactive properties of the store.

Unlike in Vuex, in Pinia you can directly reference the state value without needing to create a getter for it to get the value. This simplifies state management and reduces the amount of boilerplate code needed.

Examples

State

import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
})
});

Getters

import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
getters: {
doubleCount: (state) => state.count * 2
}
});

Actions

import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', {
state: () => ({
count: 0
}),
actions: {
increment() {
this.count++;
},
async incrementAfterDelay() {
setTimeout(() => {
this.count++;
}, 1000);
}
}
});

Using the Store in a Component

<template>
<div>
<p>Count: {{ counter.count }}</p>
<p>Double Count: {{ counter.doubleCount }}</p>
<button @click="counter.increment">Increment</button>
<button @click="counter.incrementAfterDelay">Increment After Delay</button>
</div>
</template>
<script>
import { Component, Setup, Vue} from 'vue-facing-decorator';
import { useCounterStore } from './stores/counter';
export class CounterComponent extends Vue {
@Setup(() => useCounterStore())
counter!: ReturnType<typeof useCounterStore>;
}
</script>
Table of Contents