I'm learning Vue and I noticed that I have the following syntax more or less everywhere.
export default {
components: { Navigation, View1 },
computed: {
classObject: function() {
return {
alert: this.$store.state.environment !== "dev",
info: this.$store.state.environment === "dev"
};
}
}
}
It's a pain to write out this.$store.state.donkey
all the time and it lowers the readability too. I'm sensing that I'm doing it in a less than optimal way. How should I refer to the state of the store?
you can set computed properties for both states & getters i.e.
computed: {
donkey () {
this.$store.state.donkey
},
ass () {
this.$store.getters.ass
},
...
Whilst you still need to call the $state.store once you can then reference a donkey or an ass on your vm...
To make things even easier you can pull in the vuex map helpers and use them to find your ass ... or donkey:
import { mapState, mapGetters } from 'vuex'
default export {
computed: {
...mapState([
'donkey',
]),
...mapGetters([
'ass',
]),
...mapGetters({
isMyAss: 'ass', // you can also rename your states / getters for this component
}),
now if you look at this.isMyAss
you'll find it ... your ass
worth noting here that getters, mutations & actions are global - therefore they are referenced directly on your store, i.e.
store.getters
,store.commit
&store.dispatch
respectively. This applies whether they are in a module or in the root of your store. If they are in a module check out namespacing to prevent overwriting previously used names: vuex docs namespacing. However if you are referencing a modules state, you must prepend the name of the module, i.e.store.state.user.firstName
in this exampleuser
is a module.
Edit 23/05/17
Since the time of writing Vuex has been updated and its namespacing feature is now a go to when you work with modules. Simply add namespace: true
to your modules export, i.e.
# vuex/modules/foo.js
export default {
namespace: true,
state: {
some: 'thing',
...
add the foo
module to your vuex store:
# vuex/store.js
import foo from './modules/foo'
export default new Vuex.Store({
modules: {
foo,
...
then when you are pulling this module into your components you can:
export default {
computed: {
...mapState('foo', [
'some',
]),
...mapState('foo', {
another: 'some',
}),
...
this makes modules very simple and clean to use, and is a real saviour if you are nesting them multiple levels deep: namespacing vuex docs
I have put together an example fiddle to showcase the various ways you can reference and work with your vuex store:
JSFiddle Vuex Example
Or check out the below:
const userModule = {
state: {
firstName: '',
surname: '',
loggedIn: false,
},
// @params state, getters, rootstate
getters: {
fullName: (state, getters, rootState) => {
return `${state.firstName} ${state.surname}`
},
userGreeting: (state, getters, rootState) => {
return state.loggedIn ? `${rootState.greeting} ${getters.fullName}` : 'Anonymous'
},
},
// @params state
mutations: {
logIn: state => {
state.loggedIn = true
},
setName: (state, payload) => {
state.firstName = payload.firstName
state.surname = payload.surname
},
},
// @params context
// context.state, context.getters, context.commit (mutations), context.dispatch (actions)
actions: {
authenticateUser: (context, payload) => {
if (!context.state.loggedIn) {
window.setTimeout(() => {
context.commit('logIn')
context.commit('setName', payload)
}, 500)
}
},
},
}
const store = new Vuex.Store({
state: {
greeting: 'Welcome ...',
},
mutations: {
updateGreeting: (state, payload) => {
state.greeting = payload.message
},
},
modules: {
user: userModule,
},
})
Vue.component('vuex-demo', {
data () {
return {
userFirstName: '',
userSurname: '',
}
},
computed: {
loggedInState () {
// access a modules state
return this.$store.state.user.loggedIn
},
...Vuex.mapState([
'greeting',
]),
// access modules state (not global so prepend the module name)
...Vuex.mapState({
firstName: state => state.user.firstName,
surname: state => state.user.surname,
}),
...Vuex.mapGetters([
'fullName',
]),
...Vuex.mapGetters({
welcomeMessage: 'userGreeting',
}),
},
methods: {
logInUser () {
this.authenticateUser({
firstName: this.userFirstName,
surname: this.userSurname,
})
},
// pass an array to reference the vuex store methods
...Vuex.mapMutations([
'updateGreeting',
]),
// pass an object to rename
...Vuex.mapActions([
'authenticateUser',
]),
}
})
const app = new Vue({
el: '#app',
store,
})
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex"></script>
<div id="app">
<!-- inlining the template to make things easier to read - all of below is still held on the component not the root -->
<vuex-demo inline-template>
<div>
<div v-if="loggedInState === false">
<h1>{{ greeting }}</h1>
<div>
<p><label>first name: </label><input type="text" v-model="userFirstName"></p>
<p><label>surname: </label><input type="text" v-model="userSurname"></p>
<button :disabled="!userFirstName || !userSurname" @click="logInUser">sign in</button>
</div>
</div>
<div v-else>
<h1>{{ welcomeMessage }}</h1>
<p>your name is: {{ fullName }}</p>
<p>your firstName is: {{ firstName }}</p>
<p>your surname is: {{ surname }}</p>
<div>
<label>Update your greeting:</label>
<input type="text" @input="updateGreeting({ message: $event.target.value })">
</div>
</div>
</div>
</vuex-demo>
</div>
As you can see if you wanted to pull in mutations or actions this would be done in a similar way but in your methods using mapMutations
or mapActions
Adding Mixins
To extend the above behaviour you could couple this with mixins and then you'd only have to set up the above computed properties once and pull in the mixin on the component that needs them:
animals.js (mixin file)
import { mapState, mapGetters } from 'vuex'
export default {
computed: {
...mapState([
'donkey',
...
your component
import animalsMixin from './mixins/animals.js'
export default {
mixins: [
animalsMixin,
],
created () {
this.isDonkeyAnAss = this.donkey === this.ass
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With