Vuex registerModule method registers an empty module - vue.js

I have two modules. One load statically, the other dynamically.
StaticLoadingStore.js:
export default {
namespaced: false,
state() {
return {
propertySL: 'Some value from a statically loaded module',
}
},
getters: {
getPropertySL(state) {
return state.propertySL
},
},
}
DynamicLoadingStore.js
export default {
namespaced: true,
state() {
return {
propertyDL: 'Some value from a dynamically loaded module',
}
},
getters: {
getPropertyDL(state) {
return state.propertyDL
},
},
}
Dynamically loaded module shows that it is empty. Why?
HelloWorld.vue:
<template>
<div>
<h1>SL</h1>
<h5>propertySL:</h5>
<p>{{ propertySL }}</p>
<h5>stateSL:</h5>
<code>{{stateSL}} </code>
<h1>DL</h1>
<h5>propertyDL:</h5>
<p>{{ propertyDL===undefined?'undefined':propertyDL }}</p>
<!-- return undefined -->
<h5>stateDL:</h5>
<code>{{stateDL}} </code>
<!-- return {} -->
</div>
</template>
<script>
import SLModule from '../StaticLoadingStore'
const DLModule = () => import('../DynamicLoadingStore.js');
export default {
data: () => ({
stateSL: '',
stateDL: '',
}),
computed: {
propertySL() {
return this.$store.getters['getPropertySL']
},
propertyDL() {
return this.$store.getters['dlModule/getPropertyDL']
},
},
created() {
this.$store.registerModule('slModule', SLModule);
this.stateSL = JSON.stringify(this.$store.state['slModule'], null, 2);
this.$store.registerModule('dlModule', DLModule());
this.stateDL = JSON.stringify(this.$store.state['dlModule'], null, 2);
}
}
</script>
My knowledge in vue and js is very limited, and I ask the question through Google translator, so I apologize in advance for incompetence.
Without waiting for an answer, he began to experiment.
That's how it worked.
DynamicLoadingStore.js
...
async created() {
const moduleLoader = await DLModule();
this.$store.registerModule('dlModule', moduleLoader.default);
...
But why this is not as recommended in the examples is not clear.
New problem. Reactivity does not work. alert(this.$store.getters['dlModule/getPropertyDL'])
gives expected data.
But the propertySL in template is empty. Tell me what's wrong, please.

But why this is not as recommended in the examples is not clear.
If you talking about this official guide Dynamic Module Registration. I think the author doesn't want to specify how to get the module since there are a lot of ways to do.
In your example I think both modules should call dynamic module, static module is the module that declared at store creation.
But you import it with different methods which are static import and dynamic import. You can read more about import from MDN.
To use dynamic import, there is no need to wrap import statement with function:
...
await import('../DynamicLoadingStore.js')
...
...
// This will useful when you use dynamic component
() => import('../DynamicLoadingStore.js')
...
New problem. Reactivity does not work.
alert(this.$store.getters['dlModule/getPropertyDL']) gives expected
data.
But the propertySL in template is empty. Tell me what's wrong, please.
If you register slModule before dlModule, the propertySL should still work fine but not propertyDL.
The reason is this is the how computed property works, since you are using async created instead of created, the computed property doesn't wait until async created finished. So when Vue try to compute the dependency of the property it cannot do correctly because your getters will return undefined.
You can solve this problem by use another data to trigger computed property to recompute like this:
this.dlModuleReady && this.$store.getters["dlModule/getPropertyDL"];
See example.

Related

Sendbird - Nuxt -Vuex - Do not mutate vuex store state outside mutation handlers [duplicate]

Why do I get this error:
Error [vuex] Do not mutate vuex store state outside mutation handlers.
What does it mean?
It happens when I try to type in the edit input file.
pages/todos/index.vue
<template>
<ul>
<li v-for="todo in todos">
<input type="checkbox" :checked="todo.done" v-on:change="toggle(todo)">
<span :class="{ done: todo.done }">{{ todo.text }}</span>
<button class="destroy" v-on:click="remove(todo)">delete</button>
<input class="edit" type="text" v-model="todo.text" v-todo-focus="todo == editedTodo" #blur="doneEdit(todo)" #keyup.enter="doneEdit(todo)" #keyup.esc="cancelEdit(todo)">
</li>
<li><input placeholder="What needs to be done?" autofocus v-model="todo" v-on:keyup.enter="add"></li>
</ul>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
data () {
return {
todo: '',
editedTodo: null
}
},
head () {
return {
title: this.$route.params.slug || 'all',
titleTemplate: 'Nuxt TodoMVC : %s todos'
}
},
fetch ({ store }) {
store.commit('todos/add', 'Hello World')
},
computed: {
todos () {
// console.log(this)
return this.$store.state.todos.list
}
},
methods: {
add (e) {
var value = this.todo && this.todo.trim()
if (value) {
this.$store.commit('todos/add', value)
this.todo = ''
}
},
toggle (todo) {
this.$store.commit('todos/toggle', todo)
},
remove (todo) {
this.$store.commit('todos/remove', todo)
},
doneEdit (todo) {
this.editedTodo = null
todo.text = todo.text.trim()
if (!todo.text) {
this.$store.commit('todos/remove', todo)
}
},
cancelEdit (todo) {
this.editedTodo = null
todo.text = this.beforeEditCache
},
},
directives: {
'todo-focus' (el, binding) {
if (binding.value) {
el.focus()
}
}
},
}
</script>
<style>
.done {
text-decoration: line-through;
}
</style>
stores/todos.js
export const state = () => ({
list: []
})
export const mutations = {
add (state, text) {
state.list.push({
text: text,
done: false
})
},
remove (state, todo) {
state.list.splice(state.list.indexOf(todo), 1)
},
toggle (state, todo) {
todo.done = !todo.done
}
}
Any ideas how I can fix this?
It could be a bit tricky to use v-model on a piece of state that belongs to Vuex.
and you have used v-model on todo.text here:
<input class="edit" type="text" v-model="todo.text" v-todo-focus="todo == editedTodo" #blur="doneEdit(todo)" #keyup.enter="doneEdit(todo)" #keyup.esc="cancelEdit(todo)">
use :value to read value and v-on:input or v-on:change to execute a method that perform the mutation inside an explicit Vuex mutation handler
This issue is handled here: https://vuex.vuejs.org/en/forms.html
Hello I have get the same problem and solve it with clone my object using one of the following:
{ ...obj} //spread syntax
Object.assign({}, obj)
JSON.parse(JSON.stringify(obj))
For your code I think you need to replace this part
computed: {
todos () {
// console.log(this)
return this.$store.state.todos.list
}
}
With this
computed: {
todos () {
// console.log(this)
return {...this.$store.state.todos.list}
}
}
I don't make sure if this is the best way but hope this helpful for other people that have the same issue.
This error may come from the fact you shallow cloned an object.
Meaning that you've tried to copy an object but an object is not a primitive type (like String or Number), hence it's passed by reference and not value.
Here you think that you cloned one object into the other, while you are still referencing the older one. Since you're mutating the older one, you got this nice warning.
Here is a GIF from Vue3's documentation (still relevant in our case).
On the left, it's showing an object (mug) being not properly cloned >> passed by reference.
On the right, it's properly cloned >> passed by value. Mutating this one does not mutate the original
The proper way to manage this error is to use lodash, this is how to load it efficiently in Nuxt:
Install lodash-es, eg: yarn add lodash-es, this is an optimized tree-shakable lodash ES module
you may also need to transpile it in your nuxt.config.js with the following
build: {
transpile: ['lodash-es'],
}
load it into your .vue components like this
<script>
import { cloneDeep } from 'lodash-es'
...
const properlyClonedObject = cloneDeep(myDeeplyNestedObject)
...
</script>
Few keys points:
lodash is recommended over JSON.parse(JSON.stringify(object)) because it does handle some edge-cases
we only load small functions from lodash and not the whole library thanks to this setup, so there is no penalty in terms of performance
lodash has a lot of well battle-tested useful functions, which is heavily lacking in JS (no core library)
UPDATE: structuredClone is also native and quite performant if you're looking for a solution for a deep copy, bypassing the need for Lodash at all.
There is no headache if you can use lodash
computed: {
...mapState({
todo: (state) => _.cloneDeep(state.todo)
})
}
Just in case someone's still being troubled by this,
I got my code working by making a duplicate/clone of the store state.
In your case, try something like this...
computed: {
todos () {
return [ ...this.$store.state.todos.list ]
}
}
It's basically a spread operator which results in making a clone of the todos.list array. With that, you're not directly changing the values of your state, just don't forget commit so your mutations will be saved in the store.
export default new Vuex.Store({
...
strict: true
})
try to comment "strict"
If you are using Vuex Modules, you might bump into this error if your module's data property is an object, instead of a function that returns an object, and you are sharing this Module between more than one Store.
So instead of:
// In stores/YourModule.js
export default {
state: { name: 'Foo' },
}
Change it to:
// In stores/YourModule.js
export default {
state: () => {
return { name: 'Foo' };
},
}
This is actually documented here:
Sometimes we may need to create multiple instances of a module, for
example:
Creating multiple stores that use the same module (e.g. To avoid
stateful singletons in the SSR (opens new window)when the
runInNewContext option is false or 'once'); Register the same module
multiple times in the same store. If we use a plain object to declare
the state of the module, then that state object will be shared by
reference and cause cross store/module state pollution when it's
mutated.
This is actually the exact same problem with data inside Vue
components. So the solution is also the same - use a function for
declaring module state (supported in 2.3.0+):
If your data is an array with objects inside. Below snippet is the solution
const toyData = await this.$store.dispatch(
`user/fetchCoinToys`,
payload
)
const msgList = toyData.msglist.map((data) => {
return { ...data }
})
I had to add mutation and call it instead of setting directly.
wrong:
someAction({state, rootState}) {
state.someValue = true;
}
right:
mutations: {
...
setSomeValue(state, val) {
state.someValue = val;
},
...
}
...
someAction({state, commit, rootState}) {
commit('setSomeValue', true);
}
It is not your case but if someone is using typescript and is having the same problem, adding this: any as the first param in your method or somewhere else should fix the problem

Parametized getter in Vuex - trigger udpate

My Vuex store has a collection of data, say 1,000 records. There is a getter with a parameter, getItem, taking an ID and returning the correct record.
I need components accessing that getter to know when the data is ready (when the asynchronous fetching of all the records is done).
However since it's a parametized getter, Vue isn't watching the state it depends on to know when to update it. What should I do?
I keep wanting to revert to a BehaviorSubject pattern I used in Angular a lot, but Vuex + rxJS seems heavy for this, right?
I feel I need to somehow emit a trigger for the getter to recalculate.
store.js
import Vue from 'vue'
import Vuex from 'vuex'
import VueResource from 'vue-resource'
Vue.use(VueResource);
Vue.use(Vuex)
export default new Vuex.Store({
state: {
numberOfPosts : -1,
posts : {}, //dictionary keyed on slug
postsLoaded : false,
},
getters : {
postsLoaded : function(state){
return state.postsLoaded;
},
totalPosts : function(state){
return state.numberOfPosts;
},
post: function( state ){
return function(slug){
if( state.posts.hasOwnProperty( slug ) ){
return state.posts.slug;
}else{
return null;
}
}
}
},
mutations: {
storePosts : function(state, payload){
state.numberOfPosts = payload.length;
for( var post of payload ){
state.posts[ post.slug ] = post;
}
state.postsLoaded = true;
}
},
actions: {
fetchPosts(context) {
return new Promise((resolve) => {
Vue.http.get(' {url redacted} ').then((response) => {
context.commit('storePosts', response.body);
resolve();
});
});
}
}
})
Post.vue
<template>
<div class="post">
<h1>This is a post page for {{ $route.params.slug }}</h1>
<div v-if="!postsLoaded">LOADING</div>
<div v-if="postNotFound">No matching post was found.</div>
<div v-if="postsLoaded && !postNotFound" class="post-area">
{{ this.postData.title.rendered }}
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'post',
data : function(){
return {
loading : true,
postNotFound : false,
postData : null
}
},
mounted : function(){
this.postData = this.post( this.$route.params.slug );
if ( this.postData == null ){
this.postNotFound = true;
}
},
computed : mapGetters([
'postsLoaded',
'post'
])
}
</script>
As it stands, it shows the "post not found" message because when it accesses the getter, the data isn't ready yet. If a post isn't found, I need to distinguish between (a) the data is loaded and there isn't a post that matches, and (b) the data isn't loaded so wait
I suspect the problem lies with how your are setting the posts array in your storePosts mutation, specifially this line:
state.posts[ post.slug ] = post
VueJs can't track that operation so has no way of knowing that the array has updated, thus your getter is not updated.
Instead your need to use Vue set like this:
Vue.set(state.posts, post.slug, post)
For more info see Change Detection Caveats documentation
code sample of mark's answer
computed: {
...mapGetters([
'customerData',
])
},
methods: {
...mapActions(['customerGetRecords']),
},
created() {
this.customerGetRecords({
url: this.currentData
});
Sorry I can't use code to illustrate my idea as there isn't a running code snippet for now. I think what you need to do is that:
Access the vuex store using mapGetters in computed property, which you already did in Post.vue.
Watch the mapped getters property inside your component, in your case there would be a watcher function about postsLanded or post, whatever you care about its value changes. You may need deep or immediate property as well, check API.
Trigger mutations to the vuex store through actions, and thus would change the store's value which your getters will get.
After the watched property value changes, the corresponding watch function would be fired with old and new value and you can complete your logic there.

Can be exclude `this` keyword in vue.js application?

Actually I am following Douglas Crockford jslint .
It give warning when i use this.
[jslint] Unexpected 'this'. (unexpected_a) 
I can not see any solution around for the error . Don't say add this in jslist.options and mark it true.
Is there is any approach without using this?
EDIT
ADDED CODE
// some vue component here
<script>
export default {
name: "RefereshPage",
data() {
return {
progressValue: 0
}
},
methods:{
getRefreshQueue(loader){
console.log(this.progressValue); // ERROR come here [jslint] Unexpected 'this'. (unexpected_a) 
}
}
}
</script>
Check out this jsfiddle. How can you avoid using this?
https://jsfiddle.net/himmsharma99/ctj4sm7f/5/
as i already stated in the comments:
using this is an integral part of how vue.js works within a component. you can read more about how it proxies and keeps track of dependencies here: https://v2.vuejs.org/v2/api/#Options-Data
As others have said, you're better off just disabling your linter or switching to ESLint. But if you insist on a workaround, you could use a mixin and the $mount method on a vue instance to avoid using this altogether ..
let vm;
const weaselMixin = {
methods: {
getdata() {
console.log(vm.users.foo.name);
}
},
mounted: function () {
vm.getdata();
}
};
vm = new Vue({
mixins: [weaselMixin],
data: {
users: {
foo: {
name: "aa"
}
}
}
});
vm.$mount('#app');
See the modified JSFiddle
As you can see, this only complicates what should be a fairly simple component. It only goes to show that you shouldn't break the way vue works just to satisfy your linter.
I would suggest you go through this article. Particularly important is this part ..
Vue.js proxies our data and methods to be available in the this context. So by writing this.firstName, we can access the firstName property within the data object. Note that this is required.
In the code you posted, you appear to be missing a } after getRefreshQueue definition. It's not the error your linter is describing, but maybe it got confused by the syntax error?
It is possible using the new Composition API. Vue 3 will have in-built support, but you can use this package for Vue 2 support.
An example of a component without this (from vuejs.org):
<template>
<button #click="increment">
Count is: {{ state.count }}, double is: {{ state.double }}
</button>
</template>
<script>
import { reactive, computed } from 'vue'
export default {
setup() {
const state = reactive({
count: 0,
double: computed(() => state.count * 2)
})
function increment() {
state.count++
}
return {
state,
increment
}
}
}
</script>

Property or method "$v" is not defined using Vuelidate

Error:
[Vue warn]: Property or method "$v" is not defined on the instance but
referenced during render. Make sure that this property is reactive,
either in the data option, or for class-based components, by
initializing the property. See:
https://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
found in
---> at resources\assets\js\components\products\Product_create.vue
I'm using Vue.js and Vuelidate as validator,
I've copied and pasted this code from here https://jsfiddle.net/Frizi/b5v4faqf/ but it still doesn't work :
Vue Component :
<template >
<input v-model="$v.text.$model" :class="status($v.text)">
<!-- <pre>{{ $v }}</pre> -->
</template>
<script>
import { required, minLength, between } from 'vuelidate/lib/validators'
export default {
data() {
return {
text: ''
}
},
validations: {
text: {
required,
minLength: minLength(5)
}
},
methods: {
status(validation) {
return {
error: validation.$error,
dirty: validation.$dirty
}
}
}
}
</script>
App.js
require('./bootstrap');
window.Vue = require('vue');
window.VueRouter = require('vue-router').default;
window.VueAxios = require('vue-axios').default;
window.Axios = require('axios').default;
window.VueFormWizard = require('vue-form-wizard');
window.Vuelidate = require('vuelidate').default;
import 'vue-form-wizard/dist/vue-form-wizard.min.css';
Vue.use(VueRouter,VueAxios,axios,VueFormWizard,Vuelidate);
const ProductOptionCreate = Vue.component('po-create',require('./components/products/ProductOption_create.vue'));
const ProgressModal = Vue.component('vue-modal',require('./components/ProgressModal.vue'));
const ProductIndex = Vue.component('product-list',require('./components/products/Product_index.vue'));
const productshow = Vue.component('productshow',require('./components/products/ProductOption_show.vue'));
const ProductCreate = Vue.component('product-create',require('./components/products/Product_create.vue'));
const app = new Vue({
el:'#app',
});
What's wrong with this code?
The problem is that $v is not defined at a component level, and it is because of the order of your components, you need to reorder them like so:
// ... other stuff
import 'vue-form-wizard/dist/vue-form-wizard.min.css';
Vue.use(Vuelidate);
const ProductOptionCreate = // ... rest of your components
I think the problem was that the validation was declared within the data property of the component and not as a direct property of the component.
So
export default {
validations: {
text: {
required,
minLength: minLength(5)
},
data() {
return {
text: ''
}
},
},
........
instead of
export default {
data() {
return {
text: ''
}
},
validations: {
text: {
required,
minLength: minLength(5)
}
The reason $v is not available on your instance is because you haven't instantiated the Vuelidate plugin. And that's because you tried to streamline the call to Vue.use().
Your current Vue.use() call only instantiates the first plugin (VueRouter) and passes VueAxios plugin object as VueRouter's config object. All subsequent arguments are ignored.
To streamline your calls to Vue.use(), you can't simply add more arguments to it.
Instead of this erroneous syntax (which breaks instantiation of all but first plugin):
Vue.use(VueRouter, VueAxios, axios, VueFormWizard, Vuelidate);
... you could use:
[[VueRouter], [VueAxios, axios], [VueFormWizard], [Vuelidate]]
.forEach(args => Vue.use(...args));
With Typescript: unfortunately, TS parser wrongly includes 0 arguments as a possible outcome of the spread operator in the above case, so you'd need to suppress it using:
[
[VueRouter],
[VueAxios, axios],
[VueFormWizard],
[Vuelidate]
/* #ts-ignore: TS2557, TS wrong about array above having empty members */
].forEach(args => Vue.use(...args));
... at least for now ("typescript": "^3.9.7").
The above syntax is the exact equivalent of:
Vue.use(VueRouter);
Vue.use(VueAxios, axios);
Vue.use(VueFormWizard);
Vue.use(Vuelidate); // <= this is what you were missing, basically
// but your syntax also broke VueAxios and VueFormWizard install
On a personal note: although a tad more repetitive, I actually find the Vue.use() syntax cleaner in this case (more readable).
The validations should be defined in a component in order to initialize this.$v
I had a typo and didn't realized I was declaring validations inside methods.
methods: {
validations: {
isUScitizen: { required },
},
}
And I was trying to access this.$v which was undefined because the component didn't had validations defined.
You must specify this.$v and there will be no error

Vuex - Do not mutate vuex store state outside mutation handlers

Why do I get this error:
Error [vuex] Do not mutate vuex store state outside mutation handlers.
What does it mean?
It happens when I try to type in the edit input file.
pages/todos/index.vue
<template>
<ul>
<li v-for="todo in todos">
<input type="checkbox" :checked="todo.done" v-on:change="toggle(todo)">
<span :class="{ done: todo.done }">{{ todo.text }}</span>
<button class="destroy" v-on:click="remove(todo)">delete</button>
<input class="edit" type="text" v-model="todo.text" v-todo-focus="todo == editedTodo" #blur="doneEdit(todo)" #keyup.enter="doneEdit(todo)" #keyup.esc="cancelEdit(todo)">
</li>
<li><input placeholder="What needs to be done?" autofocus v-model="todo" v-on:keyup.enter="add"></li>
</ul>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
data () {
return {
todo: '',
editedTodo: null
}
},
head () {
return {
title: this.$route.params.slug || 'all',
titleTemplate: 'Nuxt TodoMVC : %s todos'
}
},
fetch ({ store }) {
store.commit('todos/add', 'Hello World')
},
computed: {
todos () {
// console.log(this)
return this.$store.state.todos.list
}
},
methods: {
add (e) {
var value = this.todo && this.todo.trim()
if (value) {
this.$store.commit('todos/add', value)
this.todo = ''
}
},
toggle (todo) {
this.$store.commit('todos/toggle', todo)
},
remove (todo) {
this.$store.commit('todos/remove', todo)
},
doneEdit (todo) {
this.editedTodo = null
todo.text = todo.text.trim()
if (!todo.text) {
this.$store.commit('todos/remove', todo)
}
},
cancelEdit (todo) {
this.editedTodo = null
todo.text = this.beforeEditCache
},
},
directives: {
'todo-focus' (el, binding) {
if (binding.value) {
el.focus()
}
}
},
}
</script>
<style>
.done {
text-decoration: line-through;
}
</style>
stores/todos.js
export const state = () => ({
list: []
})
export const mutations = {
add (state, text) {
state.list.push({
text: text,
done: false
})
},
remove (state, todo) {
state.list.splice(state.list.indexOf(todo), 1)
},
toggle (state, todo) {
todo.done = !todo.done
}
}
Any ideas how I can fix this?
It could be a bit tricky to use v-model on a piece of state that belongs to Vuex.
and you have used v-model on todo.text here:
<input class="edit" type="text" v-model="todo.text" v-todo-focus="todo == editedTodo" #blur="doneEdit(todo)" #keyup.enter="doneEdit(todo)" #keyup.esc="cancelEdit(todo)">
use :value to read value and v-on:input or v-on:change to execute a method that perform the mutation inside an explicit Vuex mutation handler
This issue is handled here: https://vuex.vuejs.org/en/forms.html
Hello I have get the same problem and solve it with clone my object using one of the following:
{ ...obj} //spread syntax
Object.assign({}, obj)
JSON.parse(JSON.stringify(obj))
For your code I think you need to replace this part
computed: {
todos () {
// console.log(this)
return this.$store.state.todos.list
}
}
With this
computed: {
todos () {
// console.log(this)
return {...this.$store.state.todos.list}
}
}
I don't make sure if this is the best way but hope this helpful for other people that have the same issue.
This error may come from the fact you shallow cloned an object.
Meaning that you've tried to copy an object but an object is not a primitive type (like String or Number), hence it's passed by reference and not value.
Here you think that you cloned one object into the other, while you are still referencing the older one. Since you're mutating the older one, you got this nice warning.
Here is a GIF from Vue3's documentation (still relevant in our case).
On the left, it's showing an object (mug) being not properly cloned >> passed by reference.
On the right, it's properly cloned >> passed by value. Mutating this one does not mutate the original
The proper way to manage this error is to use lodash, this is how to load it efficiently in Nuxt:
Install lodash-es, eg: yarn add lodash-es, this is an optimized tree-shakable lodash ES module
you may also need to transpile it in your nuxt.config.js with the following
build: {
transpile: ['lodash-es'],
}
load it into your .vue components like this
<script>
import { cloneDeep } from 'lodash-es'
...
const properlyClonedObject = cloneDeep(myDeeplyNestedObject)
...
</script>
Few keys points:
lodash is recommended over JSON.parse(JSON.stringify(object)) because it does handle some edge-cases
we only load small functions from lodash and not the whole library thanks to this setup, so there is no penalty in terms of performance
lodash has a lot of well battle-tested useful functions, which is heavily lacking in JS (no core library)
UPDATE: structuredClone is also native and quite performant if you're looking for a solution for a deep copy, bypassing the need for Lodash at all.
There is no headache if you can use lodash
computed: {
...mapState({
todo: (state) => _.cloneDeep(state.todo)
})
}
Just in case someone's still being troubled by this,
I got my code working by making a duplicate/clone of the store state.
In your case, try something like this...
computed: {
todos () {
return [ ...this.$store.state.todos.list ]
}
}
It's basically a spread operator which results in making a clone of the todos.list array. With that, you're not directly changing the values of your state, just don't forget commit so your mutations will be saved in the store.
export default new Vuex.Store({
...
strict: true
})
try to comment "strict"
If you are using Vuex Modules, you might bump into this error if your module's data property is an object, instead of a function that returns an object, and you are sharing this Module between more than one Store.
So instead of:
// In stores/YourModule.js
export default {
state: { name: 'Foo' },
}
Change it to:
// In stores/YourModule.js
export default {
state: () => {
return { name: 'Foo' };
},
}
This is actually documented here:
Sometimes we may need to create multiple instances of a module, for
example:
Creating multiple stores that use the same module (e.g. To avoid
stateful singletons in the SSR (opens new window)when the
runInNewContext option is false or 'once'); Register the same module
multiple times in the same store. If we use a plain object to declare
the state of the module, then that state object will be shared by
reference and cause cross store/module state pollution when it's
mutated.
This is actually the exact same problem with data inside Vue
components. So the solution is also the same - use a function for
declaring module state (supported in 2.3.0+):
If your data is an array with objects inside. Below snippet is the solution
const toyData = await this.$store.dispatch(
`user/fetchCoinToys`,
payload
)
const msgList = toyData.msglist.map((data) => {
return { ...data }
})
I had to add mutation and call it instead of setting directly.
wrong:
someAction({state, rootState}) {
state.someValue = true;
}
right:
mutations: {
...
setSomeValue(state, val) {
state.someValue = val;
},
...
}
...
someAction({state, commit, rootState}) {
commit('setSomeValue', true);
}
It is not your case but if someone is using typescript and is having the same problem, adding this: any as the first param in your method or somewhere else should fix the problem