Introduction
Vanilla, strongly-typed store accessor.
The accessor serves two purposes:
-
It wraps the store so that it can be typed without conflicting with the default types for
$store
in a Nuxt project. -
It allows us to avoid creating impossible type definitions for namespaced magic strings, like
commit('mysubmodule/mutation')
.
Structure
-
Getters, state, mutations and actions are flattened.
-
Getters take priority over state (so state is not included if a getter of the same name exists).
-
Modules are namespaced.
Cannot set property <name> of #<Object> which has only a getter.
So, for example:
// Accesses this.$store.state.email
this.$accessor.email
// Accesses this.$store.getters['fullName']
this.$accessor.fullName
// Runs this.$store.dispatch('initialiseStore')
this.$accessor.initialiseStore()
// Runs this.$store.commit('SET_NAME', 'John Doe')
this.$accessor.SET_NAME('John Doe')
// Accesses this.$store.state.submodule.id
this.$accessor.submodule.id
// etc.
Typing the accessor
Adding types is simple. A helper function, getAccessorType
, is provided, which compiles to nothing and only serves to return the correct type of the accessor so that it can be used where you see fit.
Make sure you define types correctly following these instructions.
Using the accessor
Components, fetch
and asyncData
import Vue from 'vue'
export default Vue.extend({
fetch({ app: { $accessor } }) {
$accessor.fetchItems()
},
asyncData({ app: { $accessor } }) {
return {
myEmail: $accessor.email,
}
},
computed: {
email() {
// This (behind the scenes) returns this.$store.getters['email']
return this.$accessor.email
},
},
methods: {
resetEmail() {
// Executes this.$store.dispatch('submodule/resetEmail', 'new@email.com')
this.$accessor.submodule.resetEmail('new@email.com')
},
},
})
Composition API
import { defineComponent, computed } from '@nuxtjs/composition-api'
export default defineComponent({
setup(_props, { root }) {
const counter = computed(() => root.$accessor.counter)
return {
counter,
}
},
})
Since you are already using Composition API, you might as well create a hook dedicated to this
import { wrapProperty } from '@nuxtjs/composition-api'
export const useAccessor = wrapProperty('$accessor', false)
Should your IDE not properly recognize type declarations setup as described in these instructions, you may want to explicitly define accessor types like in the following example:
import { wrapProperty } from '@nuxtjs/composition-api'
import { accessorType } from '~/store'
export const useAccessor = (): typeof accessorType =>
wrapProperty('$accessor', false)()
Middleware
import { Context } from '@nuxt/types'
export default ({ redirect, app: { $accessor } }: Context) => {
// You can access the store here
if ($accessor.email) return redirect('/')
}
Mapping properties into your component
typed-vuex
also exports a convenience helper function to allow you to easily map the accessor into your component.
import Vue from 'vue'
// Nuxt
import { accessorType } from '~/store'
const mapper = createMapper(accessorType)
// Vue
import { accessor } from './src/store'
const mapper = createMapper(accessor)
export default Vue.extend({
computed: {
// Direct mapping to a property
...mapper('name'),
// or array of properties
...mapper(['name', 'email']),
// or submodules
...mapper('submodule', ['name', 'email']),
},
methods: {
// All your methods should be included in the methods object,
// whether they are mutations, actions (or even a method returned
// by a getter or stored in state)
...mapper('resetName'),
resetEmail() {
// You can access directly off your component instance
console.log(this.name, this.email)
this.resetName()
},
},
})