31

I am working with vuex (2.1.1) and get things working within vue single file components. However to avoid too much cruft in my vue single file component I moved some functions to a utils.js module which I import into the vue-file. In this utils.js I would like to read the vuex state. How can I do that? As it seems approaching the state with getters etc is presuming you are working from within a vue component, or not?

I tried to import state from '../store/modules/myvuexmodule' and then refer to state.mystateproperty but it always gives 'undefined', whereas in the vue-devtools I can see the state property does have proper values.

My estimate at this point is that this is simply not 'the way to go' as the state.property value within the js file will not be reactive and thus will not update or something, but maybe someone can confirm/ prove me wrong.

musicformellons
  • 12,283
  • 4
  • 51
  • 86
  • I dont fully understand as I cant see your files but vue.use(vuex) adds up vuex to all your vue components. Which could be a reason why your utils.js would not have access to it. I let external functions to be a part of actions, which take state as arguments and return the value for mutation. – Amresh Venugopal Jan 08 '17 at 15:50
  • There are no demo files as it is a 'general issue'. And yes, I do know how to solve this within a vue-file, but my question is 'how to get state in a js file'. Your 'solution' is not clear to me; it seems you are just approaching the state within the vue-file (using 'external functions'). However, I want to (if possible) approach vue state from within a js file. – musicformellons Jan 08 '17 at 16:04
  • Yes, I prefer to not alter the state externally. Can you give an idea of what kind of work you are doing in utils? Because I strongly feel using vuex actions or mutations would eliminate the need to get state outside the vuex store. – Amresh Venugopal Jan 08 '17 at 16:22
  • I edited the question. I only want to read/ evaluate in the js file the vuex state.property value (for evaluation within an if clause within a function), not alter it with actions/ mutations. – musicformellons Jan 08 '17 at 16:27

4 Answers4

40

It is possible to access the store as an object in an external js file, I have also added a test to demonstrate the changes in the state.

here is the external js file:

import { store } from '../store/store'

export function getAuth () {
  return store.state.authorization.AUTH_STATE
}

The state module:

import * as NameSpace from '../NameSpace'
/*
   Import everything in NameSpace.js as an object.
   call that object NameSpace.
   NameSpace exports const strings.
*/

import { ParseService } from '../../Services/parse'

const state = {
  [NameSpace.AUTH_STATE]: {
    auth: {},
    error: null
  }
}

const getters = {
  [NameSpace.AUTH_GETTER]: state => {
    return state[NameSpace.AUTH_STATE]
  }
}

const mutations = {
  [NameSpace.AUTH_MUTATION]: (state, payload) => {
    state[NameSpace.AUTH_STATE] = payload
  }
}

const actions = {
  [NameSpace.ASYNC_AUTH_ACTION]: ({ commit }, payload) => {
    ParseService.login(payload.username, payload.password)
      .then((user) => {
        commit(NameSpace.AUTH_MUTATION, {auth: user, error: null})
      })
      .catch((error) => {
        commit(NameSpace.AUTH_MUTATION, {auth: [], error: error})
      })
  }

export default {
  state,
  getters,
  mutations,
  actions
}

The store:

import Vue from 'vue'
import Vuex from 'vuex'
import authorization from './modules/authorization'

Vue.use(Vuex)

export const store = new Vuex.Store({
  modules: {         
    authorization    
  }                  
})                   

So far all I have done is create a js file which exports a function returning the AUTH_STATE property of authorization state variable.

A component for testing:

<template lang="html">
    <label class="login-label" for="username">Username
        <input class="login-input-field" type="text" name="username" v-model="username">
    </label>
    <label class="login-label" for="password" style="margin-top">Password
         <input class="login-input-field" type="password" name="username" v-model="password">
    </label>
    <button class="login-submit-btn primary-green-bg" type="button" @click="login(username, password)">Login</button>
</template>

<script>
import { mapActions, mapGetters } from 'vuex'
import * as NameSpace from '../../store/NameSpace'
import { getAuth } from '../../Services/test'

export default {
  data () {
    return {
      username: '',
      password: ''
    }
  },
  computed: {
    ...mapGetters({
      authStateObject: NameSpace.AUTH_GETTER
    }),
    authState () {
      return this.authStateObject.auth
    },
    authError () {
      return this.authStateObject.error
    }
  },
  watch: {
    authError () {
        console.log('watch: ', getAuth()) // ------------------------- [3]
      }
    },
    authState () {
      if (this.authState.sessionToken) {
        console.log('watch: ', getAuth()) // ------------------------- [2]
      }
    },
  methods: {
    ...mapActions({
      authorize: NameSpace.ASYNC_AUTH_ACTION
    }),
    login (username, password) {
      this.authorize({username, password})
      console.log(getAuth())             // ---------------------------[1]
    }
  }
}
</script>

On the button click default state is logged on to the console. The action in my case results in an api call, resulting a state change if the username - password combination had a record.

A success case results in showing the console in authState watch, the imported function can print the changes made to the state.

Likewise, on a fail case, the watch on authError will show the changes made to the state

Amresh Venugopal
  • 9,299
  • 5
  • 38
  • 52
  • 1
    Thanks for your answer! Looks great. I think I am importing state instead of store... I followed one of the vuex examples: https://github.com/vuejs/vuex/blob/dev/examples/shopping-cart/store/index.js It does not give the store a name, so probably I should do that!? – musicformellons Jan 08 '17 at 20:07
  • Mmmh, I think changing A: ```export default new Vuex.Store({ actions, getters, modules: { cart, products }, strict: debug, plugins: debug ? [createLogger()] : [] })``` into B: ```export const store = new Vuex.Store({ actions, getters, modules: { cart, products }, strict: debug, plugins: debug ? [createLogger()] : [] })``` will screw up my references in other files... any idea how to reference the store in case of A? – musicformellons Jan 08 '17 at 20:25
  • That is more along the lines of when to use named exports vs defaults. If the file has only one export, it is recommended to use the export default format as you see in the example link. I did that way because of my habit to use named exports everywhere. – Amresh Venugopal Jan 08 '17 at 20:25
  • 4
    So if you have used the 'A' format then the name by which you are importing like `import someName from '../store'` should be the store. Do note that it is `someName` and not `{ someName }` in the import statement – Amresh Venugopal Jan 08 '17 at 20:28
  • 1
    Thanks for the detailed answer, very helpful. I am using the `export default` syntax in my Vuex store and it took a little experimentation to get the syntax just right, so am sharing a summary here. So my Vuex store looks like: ./store/index.js: `export default new Vuex.Store({...});` And then to reference in my .js file: `import store from '../store/index'; ... const test = store.state.someGetter;` (Blech trying to format code in a comment...) – Dave Marley Mar 29 '20 at 21:05
8

For anyone wondering how to access a mutation from a javascript file, you can do the following:

import store from './store'
store.commit('mutation_name', mutation_argument);

Or for actions,

store.dispatch('action_name', action_argument)
Nic Scozzaro
  • 6,651
  • 3
  • 42
  • 46
5
import store from './store'

and than

store.commit('mutation_name', mutation_argument)

if you use js file

Serhii
  • 96
  • 1
  • 7
1

You can also access actions like:

import store from './store'
store.dispatch('action_name', action_argument)
Stefano Nepa
  • 491
  • 1
  • 6
  • 13