Vuex module namespace not found in mapActions() - vue.js

I'm getting the following error while trying to call an action from my store:
[vuex] module namespace not found in mapActions():
feedbacksessionStore/
From other solutions that I found online people were suggesting to set 'namespaced: true', however it doesn't help for my case somehow.
Here is the snippet of my store code:
export const feedbackSessionStore = {
namespaced: true,
state: {
feedback_sessions: {},
},
actions: {
async createFeedbackSession({commit, state}, { data }) {
// some code
}
}
}
And the snippet of the component code:
import { mapGetters, mapState, mapActions } from 'vuex'
// some code
export default {
name: 'create-edit-feedback-session',
methods: {
...mapActions('feedbackSessionStore', [
'createFeedbackSession'
]),
// some code
}

As a solution to this problem you have to do tow things:
make a 'feedbackSessionStore.js' as a separate module by doing this code in store/modules directory:
namespaced: true,
state: {
feedback_sessions: {},
},
actions: {
async createFeedbackSession({commit, state}, { data }) {
// some code
}
}
add this module to the store/index.js like that:
import * as feedbackSessionStore from "#/store/modules/feedbackSessionStore.js";
after these two steps it should work.

In addition to El-Hani's answer, inside your store folder there must be an index.js file which contains your store modules. In that file import the module and register it.
// store/index.js
import 'feedbackSessionStore' from './modules/feedbackSessionStore.js'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
feedbackSessionStore // <- Register here, and mapAction name becomes this
}
}
And check if the pointed mapAction name is exactly the same with here.

Related

How do I commit a vuex store mutation from inside a vue-router route that imports "store"?

My goal is to commit (invoke/call) a mutation that I've defined in my Vuex store.
store/store.js
export default {
modules: {
app: {
state: {
shouldDoThing: false,
}
mutations: {
setShouldDoThing: (state, doThing) => { state.shouldDoThing = doThing },
}
}
}
}
Since I attach Vuex to my app, I can use this.$store.commit throughout the app in various components without issue.
main.js
import Store from 'store/store.js';
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const app = new Vue({
el: '#app-root',
store,
// ...etc
});
For example:
exampleComponent.vue
export default {
created() {
// This works!
this.$store.commit('setShouldDoThing', true);
},
}
Now I'd like to commit something from a vue-router Route file, in a beforeEnter method:
exampleRoute.js
import Store from 'store/store.js'
const someRoute = {
path: '/blah',
beforeEnter(to, from, next) {
Store.commit('setShouldDoThing', true);
next();
}
}
However, when I try the above, I get the error
TypeError: _state_store__WEBPACK_IMPORTED_MODULE_10__.default.commit is not a function
There's lots of examples online of successfully using vuex getters by importing. And, if I console.log() the Store import, I can see my entire store structure
modules:
app:
actions: {someAction: ƒ, …}
getters: {isStartingQuery: ƒ}
mutations: {ariaAnnounce: ƒ, …}
state: {…}
__proto__: Object
How can I import my Store and then commit a mutation from within a vue-router file?
I've been googling for a very long time, and didn't find a stackoverflow answer or a vue forums answer for this specific case or issue, so below is the solution that I tested and works in my case.
For whatever reason, I can't trigger commit. However, I can simply invoke the mutation directly, and this change is then reflected throughout other components (as in, a "different" store wasn't imported).
someRoute.js
import Store from 'store/store.js'
const someRoute = {
path: '/blah',
beforeEnter(to, from, next) {
Store.modules.app.mutations.setShouldDoThing(Store.modules.app.state, true);
next();
}
}
And later, in some component:
someComponent.vue
export default {
beforeMount() {
console.log(this.$store.state.app.shouldDoThing);
// true
}
}

"[vuex] state field foo was overridden by a module with the same name at foobar" with deepmerge helper function in jest

I'm using a helper function to create a store inside my jests. The helper function uses deepmerge to merge the basic configuration with a customized configuration. This results in multiple console warnings
[vuex] state field "cart" was overridden by a module with the same name at "cart"
[vuex] state field "customer" was overridden by a module with the same name at "customer"
[vuex] state field "checkout" was overridden by a module with the same name at "checkout"
store.js (Reduced to a minimum for presentation purpose)
import cart from './modules/cart'
import checkout from './modules/checkout'
import customer from './modules/customer'
Vue.use(Vuex)
export const config = {
modules: {
cart,
customer,
checkout,
},
}
export default new Vuex.Store(config)
test-utils.js
import merge from 'deepmerge'
import { config as storeConfig } from './vuex/store'
// merge basic config with custom config
export const createStore = config => {
const combinedConfig = (config)
? merge(storeConfig, config)
: storeConfig
return new Vuex.Store(combinedConfig)
}
making use of the helper function inside
somejest.test.js
import { createStore } from 'test-utils'
const wrapper = mount(ShippingComponent, {
store: createStore({
modules: {
checkout: {
state: {
availableShippingMethods: {
flatrate: {
carrier_title: 'Flat Rate',
},
},
},
},
},
}),
localVue,
})
How do I solve the console warning?
I believe the warning is somewhat misleading in this case. It is technically true, just not helpful.
The following code will generate the same warning. It doesn't use deepmerge, vue-test-utils or jest but I believe the root cause is the same as in the original question:
const config = {
state: {},
modules: {
customer: {}
}
}
const store1 = new Vuex.Store(config)
const store2 = new Vuex.Store(config)
<script src="https://unpkg.com/vue#2.6.11/dist/vue.js"></script>
<script src="https://unpkg.com/vuex#3.4.0/dist/vuex.js"></script>
There are two key parts of this example that are required to trigger the warning:
Multiple stores.
A root state object in the config.
The code in the question definitely has multiple stores. One is created at the end of store.js and the other is created by createStore.
The question doesn't show a root state object, but it does mention that the code has been reduced. I'm assuming that the full code does have this object.
So why does this trigger that warning?
Module state is stored within the root state object. Even though the module in my example doesn't explicitly have any state it does still exist. This state will be stored at state.customer. So when the first store gets created it adds a customer property to that root state object.
So far there's no problem.
However, when the second store gets created it uses the same root state object. Making a copy or merging the config at this stage won't help because the copied state will also have the customer property. The second store also tries to add customer to the root state. However, it finds that the property already exists, gets confused and logs a warning.
There is some coverage of this in the official documentation:
https://vuex.vuejs.org/guide/modules.html#module-reuse
The easiest way to fix this is to use a function for the root state instead:
state: () => ({ /* all the state you currently have */ }),
Each store will call that function and get its own copy of the state. It's just the same as using a data function for a component.
If you don't actually need root state you could also fix it by just removing it altogether. If no state is specified then Vuex will create a new root state object each time.
It is logged when a property name within the state conflicts with the name of a module, like so:
new Vuex.Store({
state: {
foo: 'bar'
},
modules: {
foo: {}
}
})
therefore this raises the warning.
new Vuex.Store(({
state: {
cart: '',
customer: '',
checkout: ''
},
modules: {
cart: {},
customer: {},
checkout: {},
}
}))
its most likely here
export const createStore = config => {
const combinedConfig = (config)
? merge(storeConfig, config)
: storeConfig
return new Vuex.Store(combinedConfig)
}
from the source code of vuex, it helps indicate where these errors are being raised for logging.
If you run the app in production, you know that this warning wont be raised... or you could potentially intercept the warning and immediately return;
vuex source code
const parentState = getNestedState(rootState, path.slice(0, -1))
const moduleName = path[path.length - 1]
store._withCommit(() => {
if (__DEV__) {
if (moduleName in parentState) {
console.warn(
`[vuex] state field "${moduleName}" was overridden by a module with the same name at "${path.join('.')}"`
)
}
}
Vue.set(parentState, moduleName, module.state)
})
vuex tests
jest.spyOn(console, 'warn').mockImplementation()
const store = new Vuex.Store({
modules: {
foo: {
state () {
return { value: 1 }
},
modules: {
value: {
state: () => 2
}
}
}
}
})
expect(store.state.foo.value).toBe(2)
expect(console.warn).toHaveBeenCalledWith(
`[vuex] state field "value" was overridden by a module with the same name at "foo.value"`
)
Well, I believe there is no need for using deepmerge in test-utils.ts. It is better we use Vuex itself to handle the merging of the module instead of merging it with other methods.
If you see the documentation for Vuex testing with Jest on mocking modules
you need to pass the module which is required.
import { createStore, createLocalVue } from 'test-utils';
import Vuex from 'vuex';
const localVue = createLocalVue()
localVue.use(Vuex);
// mock the store in beforeEach
describe('MyComponent.vue', () => {
let actions
let state
let store
beforeEach(() => {
state = {
availableShippingMethods: {
flatrate: {
carrier_title: 'Flat Rate',
},
},
}
actions = {
moduleActionClick: jest.fn()
}
store = new Vuex.Store({
modules: {
checkout: {
state,
actions,
getters: myModule.getters // you can get your getters from store. No need to mock those
}
}
})
})
});
whistle, In test cases:
const wrapper = shallowMount(MyComponent, { store, localVue })
Hope this helps!

Vuex: createNamespacedHelpers with dynamic namespace

In almost all guides, tutorial, posts, etc that I have seen on vuex module registration, if the module is registered by the component the createNamespacedHelpers are imported and defined prior to the export default component statement, e.g.:
import {createNamespacedHelpers} from 'vuex'
const {mapState} = createNamespacedHelpers('mymod')
import module from '#/store/modules/mymod'
export default {
beforeCreated() {
this.$store.registerModule('mymod', module)
}
}
this works as expected, but what if we want the module to have a unique or user defined namespace?
import {createNamespacedHelpers} from 'vuex'
import module from '#/store/modules/mymod'
export default {
props: { namespace: 'mymod' },
beforeCreated() {
const ns = this.$options.propData.namespace
this.$store.registerModule(ns, module)
const {mapState} = createNamespacedHelpers(ns)
this.$options.computed = {
...mapState(['testVar'])
}
}
}
I thought this would work, but it doesnt.
Why is something like this needed?
because
export default {
...
computed: {
...mapState(this.namespace, ['testVar']),
...
},
...
}
doesnt work
This style of work around by utilising beforeCreate to access the variables you want should work, I did this from the props passed into your component instance:
import { createNamespacedHelpers } from "vuex";
import module from '#/store/modules/mymod';
export default {
name: "someComponent",
props: ['namespace'],
beforeCreate() {
let namespace = this.$options.propsData.namespace;
const { mapActions, mapState } = createNamespacedHelpers(namespace);
// register your module first
this.$store.registerModule(namespace, module);
// now that createNamespacedHelpers can use props we can now use neater mapping
this.$options.computed = {
...mapState({
name: state => state.name,
description: state => state.description
}),
// because we use spread operator above we can still add component specifics
aFunctionComputed(){ return this.name + "functions";},
anArrowComputed: () => `${this.name}arrows`,
};
// set up your method bindings via the $options variable
this.$options.methods = {
...mapActions(["initialiseModuleData"])
};
},
created() {
// call your actions passing your payloads in the first param if you need
this.initialiseModuleData({ id: 123, name: "Tom" });
}
}
I personally use a helper function in the module I'm importing to get a namespace, so if I hadmy module storing projects and passed a projectId of 123 to my component/page using router and/or props it would look like this:
import projectModule from '#/store/project.module';
export default{
props['projectId'], // eg. 123
...
beforeCreate() {
// dynamic namespace built using whatever module you want:
let namespace = projectModule.buildNamespace(this.$options.propsData.projectId); // 'project:123'
// ... everything else as above
}
}
Hope you find this useful.
All posted answers are just workarounds leading to a code that feels verbose and way away from standard code people are used to when dealing with stores.
So I just wanted to let everyone know that brophdawg11 (one of the commenters on the issue #863) created (and open sourced) set of mapInstanceXXX helpers aiming to solve this issue.
There is also series of 3 blog posts explaining reasons behind. Good read...
I found this from veux github issue, it seems to meet your needs
https://github.com/vuejs/vuex/issues/863#issuecomment-329510765
{
props: ['namespace'],
computed: mapState({
state (state) {
return state[this.namespace]
},
someGetter (state, getters) {
return getters[this.namespace + '/someGetter']
}
}),
methods: {
...mapActions({
someAction (dispatch, payload) {
return dispatch(this.namespace + '/someAction', payload)
}
}),
...mapMutations({
someMutation (commit, payload) {
return commit(this.namespace + '/someMutation', payload)
})
})
}
}
... or maybe we don't need mapXXX helpers,
mentioned by this comment https://github.com/vuejs/vuex/issues/863#issuecomment-439039257
computed: {
state () {
return this.$store.state[this.namespace]
},
someGetter () {
return this.$store.getters[this.namespace + '/someGetter']
}
},

access store from outside js file in nuxtjs

I'm trying to access the store from a file outside a component
When I search for this problem I saw that people say that I should import the store from my file and then I can get access to it, but I can't make this work
my store is built like this:
const createStore = () => {
return new Vuex.Store({
state: { ... },
getters: { ... },
mutations: { ... },
actions: { ... },
})
}
and I've tried to import it in my js file like I saw recommended
import store from '#/store'
Any ideas?
You could import the store by registering it upon instantiation, like this:
External file some-service.js:
export default class SomeService {
constructor(store) {
this.store = store
}
doStuff() {
this.store.dispatch('someAction')
}
}

How to use shorter path to get vuex contents?

In my vuex /store/state.js I have an export default that looks like this:
export default {
foo: {
bar:{
tar: {
info: 42
}
}
}
}
So, whenever I want to access info, I usually do in my methods like this;
methods: {
getInfo () {
return this.$store.state.foo.bar.tar.info
}
}
This is just for a demo purpose, and mine is actually a bit worse, but I ended up doing the same so, I tried minimize the code using a computed prop:
computed: {
info () {
return this.$store.state.foo.bar.tar.info
}
}
Now, I just call info but still, not sure if there is a better way to get values, because sometimes I just need to call info only one in a page, so I have to use the full path or create a computed property for it.
Is there any other way to do this
I always separate vuex into separated modules. For instance if you have store for foo module. I will create file named foo.js which contains
const fooModule = {
state: {
foo: {
bar: {
tar: {
info: 42
}
}
}
},
getters: {
info (state) {
return state.foo.bar.tar.info
}
},
mutations: {
setInfo (state, payload) {
state.foo.bar.tar.info = payload
}
},
actions: {
getInfo ({commit}, payload) {
commit('setInfo', payload)
}
}
}
export default fooModule
Then in your main index vuex, import the module like this way
import Vue from 'vue'
import Vuex from 'vuex'
import fooModule from './foo.js'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
fooModule
}
})
export default store
Then if you wanna get info, you just write your code like this
import { mapGetters } from 'vuex';
export default {
computed: {
...mapGetters([
'getInfo'
])
}
}
#jefry Dewangga has the the right idea but introducing mapGetters is unnecessary.
VueX by default includes namespacing for modules which allows us to load multiple modules into a store and then reference them with or without namespacing them and it'll figure the rest out.
For Instance if we have the structure of
|-- store
|-- Modules
|-- module1.js
|-- module2.js
|-- module3.js
|-- index.js
We can use index in such a way to bind all of our modules into our Vuex store doing the following:
import Vue from 'vue'
import Vuex from 'vuex'
import modules from './modules'
Vue.use(Vuex)
export default new Vuex.Store({
modules
})
An example of our module1 could be:
const state = {
LoggedIn: true
}
const mutations = {
LOGIN(state) {
state.LoggedIn = true;
},
LOGOFF(state) {
state.LoggedIn = false;
}
}
const actions = {}
export default {
state,
mutations,
actions
}
This in turn gives us the ability to say:
this.$store.commit('LOGIN');
Note that we haven't used any namespacing but as we haven't included any duplicate mutations from within our modules were absolutely fine to implicitly declare this.
Now if we want to use namespacing we can do the following which will explicitly use out module:
this.$store.module1.commit('LOGIN');
MapGetters are useful but they provide a lot of extra overhead when we can neatly digest out modules without having to continuously map everything, unless well we find the mapping useful. A great example of when MapGetters become handy is when we are working many components down in a large project and we want to be able to look at our source without having to necessarily worry about the frontend implementation.