17
votes

In the Nuxt documentation (here) it says 'You can optionally break down a module file into separate files: state.js, actions.js, mutations.js and getters.js.'

I can't seem to find any examples of how this is done - lots of breaking down the Vuex store at the root level into state.js, actions.js, mutations.js and getters.js, and into individual module files, but nothing about breaking the modules themselves down.

So currently I have:

     ├── assets
     ├── components
     └── store
           ├── moduleOne.js
           ├── moduleTwo.js
           └── etc...

And what I would like to have is:

     ├── assets
     ├── components
     └── store
           └── moduleOne
                 └── state.js
                 └── getters.js
                 └── mutations.js
                 └── actions.js
           └── moduleTwo
                └── etc...

To try this out, in /store/moduleOne/state.js I have:

export const state = () => {
    return {
        test: 'test'
    }
};

and in /store/moduleOne/getters.js I have:

export const getters = {
    getTest (state) {
        return state.test;
    }
}

In my component I'm accessing this with $store.getters['moduleOne/getters/getTest']

However using the debugger and Vue devtools, it seems like state isn't accessible in the getters file - it seems to be looking for a state in the local file, so state.test is undefined.

Attempting to import state from my state.js file into my getters.js file doesn't seem to work either.

Does anyone have an example of how they've managed to break the store down like this in Nuxt?

3
Good question. Just tried it out and it indeed breaks down my app. Maybe create an issue on cmty.app/nuxt. Seems like either a bug or lack of documentation.Imre_G
above answer from @manniL doesn't solve the issue, since the contents of the module directory are simply repeated in the file of the same name in the root of the storeGuy Harper
Sorry, I got a bit more into detail now ;)manniL

3 Answers

22
votes

I am using nuxt 2.1.0 If you want to have something like this :

Store module Vuex with Nuxt

In my store/index.js

Make sure you have namespaced: true

import Vuex from 'vuex';
import apiModule from './modules/api-logic';
import appModule from './modules/app-logic';

const createStore = () => {
  return new Vuex.Store({
    namespaced: true,
    modules: {
      appLogic: appModule,
      api: apiModule
    }
  });
};

export default createStore

In moduleOne

In my store/api-logic/index.js

import actions from './actions';
import getters from './getters';
import mutations from './mutations';

const defaultState = {
  hello: 'salut I am module api'
}

const inBrowser = typeof window !== 'undefined';
// if in browser, use pre-fetched state injected by SSR
const state = (inBrowser && window.__INITIAL_STATE__) ? window.__INITIAL_STATE__.page : defaultState;

export default {
  state,
  actions,
  mutations,
  getters
}

In my store/api-logic/getters.js

export default {
  getHelloThere: state => state.hello
}

In module Two

In my store/app-logic/index.js

import actions from './actions';
import getters from './getters';
import mutations from './mutations';

const defaultState = {
  appLogicData: 'bonjours I am module Logic'
}

const inBrowser = typeof window !== 'undefined';
// if in browser, use pre-fetched state injected by SSR
const state = (inBrowser && window.__INITIAL_STATE__) ? window.__INITIAL_STATE__.page : defaultState;

export default {
  state,
  actions,
  mutations,
  getters
}

In my store/app-logic/getters.js

export default {
  getAppLogicData: state => state.appLogicData
}

Anywhere in the app

 computed: {
  ...mapGetters({
   logicData: 'getAppLogicData',
   coucou: 'getHelloThere'
 })
},
mounted () {
  console.log('coucou', this.coucou) --> salut I am module api
  console.log('logicData', this.logicData) --> bonjours I am module Logic
}

Bonus Point

If you want to communicate between the modules for example a action in app-logic which trigger something in api-logic. So app-logic (module one) to api-logic (module two)

When you specify root: true it will start to look at the root of the store.

In store/app-logic/actions.js

  callPokemonFromAppLogic: ({ dispatch }, id) => {
    dispatch('callThePokemonFromApiLogic', id, {root:true});
  },

In store/api-logic/actions.js

  callThePokemonFromApiLogic: ({ commit }, id) => {

    console.log('I make the call here')
    axios.get('http://pokeapi.salestock.net/api/v2/pokemon/' + id).then(response => commit('update_pokemon', response.data))
  },

In store/api-logic/index.js add another entry

import actions from './actions';
import getters from './getters';
import mutations from './mutations';

const defaultState = {
  appLogicData: 'bonjours I am module Logic',
  pokemon: {}
}

const inBrowser = typeof window !== 'undefined';
// if in browser, use pre-fetched state injected by SSR
const state = (inBrowser && window.__INITIAL_STATE__) ? window.__INITIAL_STATE__.page : defaultState;

export default {
  state,
  actions,
  mutations,
  getters
}

In store/api-logic/mutations.js add the pokemon mutation :p

  update_pokemon: (state, pokemon) => {
    state.pokemon = pokemon
  }

Anywhere in the app :

computed: {
  ...mapGetters({
    bidule: 'bidule',
    pokemon: 'getPokemon'
  })
},
mounted() {
  console.log('bidule', this.bidule)
  this.callPokemonFromAppLogic('1') --> the call 
  console.log('the pokemon', this.pokemon.name) --> 'bulbasaur'
},
methods: {
  ...mapActions({
    callPokemonFromAppLogic: 'callPokemonFromAppLogic'
  }),
}

At the end your Vue devTool should look like this :) Vue devTool screenshot store

And Voilà I hope It was clear. Code example :

https://github.com/CMarzin/nuxt-vuex-modules

4
votes

In nuxt version 2.14^ you don't necessary have to create this in your store root index.js file.

import Vuex from 'vuex';
import apiModule from './modules/api-logic';
import appModule from './modules/app-logic';

const createStore = () => {
  return new Vuex.Store({
    namespaced: true,
    modules: {
      appLogic: appModule,
      api: apiModule
    }
  });
};

export default createStore

But instead, you can just leave your root index.js file as default or do what you need. No need to import.

store/index.js

export const state = () => ({
  counter: 0
})

export const mutations = {
  increment(state) {
    state.counter++
  }
}

export const actions = {
   async nuxtServerInit({ state, commit }, { req }) {
   const cookies = this.$cookies.getAll() 
   ...
}

And this how it looks like, its very simple.

Folder structure

📦store
 ┣ 📂auth
 ┣ 📂utils
 ┣ 📂posts
 ┃ ┗ 📜actions.js
 ┃ ┗ 📜mutations.js
 ┃ ┗ 📜getters.js
 ┃ ┗ 📜index.js
 ┣ index.js

Example

store/posts/index.js you can just put the state function. You don't need to import the actions, getters and mutations.

export const state = () => ({ 
   comments: []
})

store/posts/actions.js

const actions = {
  async getPosts({ commit, state }, obj) {
    return new Promise((resolve, reject) => { 
       ...
    }
  }
}

export default actions

store/posts/mutations.js

 const mutations = {
    CLEAR_POST_IMAGE_CONTENT: (state) => {
       state.post_image_content = []
    }
 }
 
 export default mutations

store/posts/getters.js

const getters = {
    datatest: (state) => state.datatest,
    headlineFeatures: (state) => state.headlineFeatures,
}

export default getters

The effect is same as @CMarzin answer but much cleaner

0
votes

Your issue

Use default exports in your files to achieve the desired effect (no named exports except in the index.js)

Example

An example can be found directly in the Nuxt.js test suite (at https://github.com/nuxt/nuxt.js/tree/dev/test/fixtures/basic/store/foo).

If you'd run the basic fixture and access the /store page you'll see the following result

enter image description here

The "repeated" contents in the module itself just show that the split-up values take priority (otherwise getVal wouldn't return 10 but 99 and state.val wouldn't be 4 but 2).

store.vue code:

<template>
  <div>
    <h1>{{ baz }}</h1>
    <br>
    <p>{{ $store.state.counter }}</p>
    <br>
    <h2>{{ getVal }}</h2>
    <br>
    <h3>{{ getBabVal }}</h3>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
  computed: {
    ...mapGetters('foo/bar', ['baz']),
    ...mapGetters('foo/blarg', ['getVal']),
    ...mapGetters('bab', ['getBabVal'])
  }
}
</script>