Reputation: 95
I have a store.js
that I inject in my main app as follows:
new Vue({
vuetify: new Vuetify(opts),
store,
el: "#app",
},
});
My store is shaped as follows but with a lot of data in it:
store.js
export default new Vuex.Store({
state: {},
getters: {},
mutations: {},
});
I have a fairly large component within the same app that I app working on now that has a lot of state to it. Is there a way I can make it's own new store or file without changing the structure of the existing store?
I was thinking if there's a way to inject 2 stores into the app, that might help but I'm not sure if that's possible.
Upvotes: 0
Views: 357
Reputation: 1409
Using modules you can reduce the complexity of your applications. I use vuex in my projects this way:
Folder structure:
src
--components/ => folder
--store/ => folder
--App.vue
--main.js
Let's see the main.js file:
import Vue from 'vue';
import App from './App.vue'
// Import the index.js file from the store folder
import store from '@/store/index';
new Vue({
store,
render: h => h(App)
}).$mount('#app')
Now let's check the store folder structure
store/
--index.js
--moduleA/ => folder
--moduleB/ => folder
index.js file in the store folder
import Vue from 'vue';
import Vuex from 'vuex';
import moduleA from './moduleA'; // By default imports index.js file inside moduleA folder
import moduleB from './moduleB'; // By default imports index.js file inside moduleB folder
Vue.use(Vuex);
export default new Vuex.store({
modules: {
moduleA,
moduleB
}
});
Finally, let's create our modules, moduleA folder structure(same for moduleB):
moduleA/ => folder
--index.js
--actions.js
--mutations.js
--getters.js
actions.js file:
function someAction({ commit }, payload) {
commit("someMutation", 'newValue');
}
export default {
someAction
}
mutations.js file:
function someMutation(state, payload) {
state.someStateVariable = payload;
}
export default {
someMutation
}
getters.js
export default {
getSomeStateVariable: state => state.someStateVariable
}
And finally, the index.js file
import actions from './actions';
import getters from './getters'
import mutations from './mutations';
const state = {
someStateVariable: 'initial value'
}
export default {
actions,
getters,
mutations,
namespaced: true, // allows to use namespace in the components
state
}
Repeat for moduleB, and any another module if needed
And we're ready to use the store modules in our components:
<template>
<div>
{{ someStateVariable }}
<button @click="setAValue">Set a new value</button>
</div>
</template>
<script>
import { mapActions, mapGetters} from 'vuex';
export default{
name: "AComponent",
computed: {
...mapGetters({
// You can set any name as key, but the way to access the getter is
// with a string with the format "moduleName/getterName"
someStateVariable: "moduleA/getSomeStateVariable"
})
},
methods: {
...mapActions({
// Same as mapGetters
someAction: "moduleA/someAction"
}),
setAValue() {
// Actions within the mapActions helpers, can be accessed as local
// methods
this.someAction("A new value for the someStateVariable")
}
}
}
</script>
It is a lot of info, I know, so if something is not clear, please let me know and I will explain further.
Upvotes: 0
Reputation: 8329
You can use Vuex modules
It's explained pretty straightforward on the page linked at the top, but a snippet may help understand it better:
// define a module
const moduleA = {
state: () => ({
titleText: 'This is a Vuex module/namespace snippet',
items: [
'item 1',
'item 2',
'item 3'
],
}),
mutations: {
ADD_ITEM(state) {
let { items } = JSON.parse(JSON.stringify(state))
const nextItem = `item ${ items.length + 1 }`
items.push(nextItem)
state.items = items
}
},
actions: {
addItem({ commit }) {
commit('ADD_ITEM')
}
},
getters: {
getItems: ({ items }) => items,
filterItem: ({ items }) => item => {
return items.find(e => e === item)
}
}
}
// define the root store, and add the module to it
const store = new Vuex.Store({
state: {
rootItems: [
'root item 1',
'root item 2'
]
},
modules: {
moduleA: {
namespaced: true, // setting namespaced
...moduleA // spreading the module
}
}
})
new Vue({
el: "#app",
store, // only the root store is added here!
computed: {
items() {
// this getter is called in the namespace of the module
return this.$store.getters['moduleA/getItems']
},
filteredItem() {
// this getter is called in the namespace of the module and a parameter
return this.$store.getters['moduleA/filterItem']('item 1')
},
title() {
// this state is called in the namespace of the module
return this.$store.state.moduleA.titleText
},
rootItems() {
// this state is from the root store - no namespace
return this.$store.state.rootItems
},
mergedList() {
// you can mix-n-match the stores here (of course it could be solved inside Vuex - but that's a bit trickier :) )
return [...this.items, ...this.rootItems]
}
},
methods: {
addItem() {
// the action is dispatched in the namespace of the module
this.$store.dispatch('moduleA/addItem')
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vuex"></script>
<div id="app">
<div>{{ title }}</div>
<hr />
<div>MODULEA FILTERED ITEM:</div>
<div>{{ filteredItem }}</div>
<hr />
<div>MODULEA ITEM LIST:</div>
<div v-for="item in items" :key="item">
{{ item }}
</div>
<button @click="addItem">ADD ITEM</button>
<hr />
<div>ROOT ITEM LIST:</div>
<div v-for="item in rootItems" :key="item">
{{ item }}
</div>
<hr />
<div>MERGED ITEM LIST:</div>
<div v-for="item in mergedList" :key="item">
{{ item }}
</div>
<hr />
</div>
I tried to put the basic ideas in the snippet above:
mapping a state value from a namespace Vuex module
mapping a state value from the root Vuex store
mapping a getter with and without a parameter from a namespaced Vuex module
dispatching an action of a namespaced Vuex module
From this, you can see that adding a module is exactly what you are looking for: keeping the already existing store and adding another one to it.
export
the Vuex modules and import them in your root store (or make an automated import - one solution given here: Architecting Vuex store for large scale Vue.js applications)store
with this method you could break it up to logical modules, or you could just separate state
, mutations
, actions
, and getters
in distinct files.Add a module to your store
:
// store.js
import Vue from 'vue';
import Vuex from 'vuex';
import { moduleA } from './moduleA'
Vue.use(Vuex);
const initialState = () => ({})
const state = initialState()
const mutations = {}
const actions = {}
const getters = {}
export default new Vuex.Store({
state,
mutations,
actions,
getters,
modules: {
moduleA
},
});
Create & export your module (so the root store can import it):
// moduleA.js
const initialState = () => ({})
const state = initialState()
const mutations = {}
const actions = {}
const getters = {}
export const moduleA = {
namespaced: true,
state,
mutations,
actions,
getters
}
Upvotes: 1