In the store, I have an action to update some data, the action looks like this:
setRoomImage({ state }, { room, index, subIndex, image }) {
state.fullReport.rooms[room].items[index].items[subIndex].image = image;
console.log(state.fullReport.rooms[room].items[index].items[subIndex])
},
Because all of this data is dynamic so I have to dynamically change the nested values and can't directly hard code the properties. The data looks like this:
fullreport: {
rooms: {
abc: {
items: [
{
type: "image-only",
items: [
{
label: "Main Image 1",
image: ""
},
{
label: "Main Image 2",
image: ""
}
]
}
]
}
}
}
When I dispatch the action, In the console I can see that the value of the sub-property image
is successfully mutated, but if I access the VueX store from the Vue DevTools inside Chrome, I see that value doesn't change there. Here is the console output:
Please, can somebody tell why is it happening? As I know that data is successfully changing, but somehow the state isn't showing it and hence my components do not rerender.
I also tried using Vue.set
instead of simple assignment, but still no luck :(
Vue.set(
state.fullReport.rooms[room].items[index].items[subIndex],
"image",
image
);
Following David Gard's answer, I tried the following:
I am also using Lodash _
(I know making whole copies of objects isn't good), this is the mutation code block.
let fullReportCopy = _.cloneDeep(state.fullReport);
fullReportCopy.rooms[room].items[index].items[subIndex].image = image;
Vue.set(state, "fullReport", fullReportCopy);
Now In the computed property, where the state.fullReport
is a dependency, I have a console.log
which just prints out a string whenever the computed property is re-computed.
Every time I commit this mutation, I see the computed property logs the string, but the state it is receiving still doesn't change, I guess Vue.set
just tells the computed property that the state is changed, but it doesn't actually change it. Hence there is no change in my component's UI.
The issue is, that you have to fill Arrays and Objects in two different ways, hence, consider whether you need access to their native methods or not. Unfortunately Vuex does not support reactive Maps yet.
That aside, I also work with projects that require the dynamic setting of properties with multiple nested levels. One way to go about it is to recursively set each property.
function createReactiveNestedObject(rootProp, object) {
// root is your rootProperty; e.g. state.fullReport
// object is the entire nested object you want to set
let root = rootProp;
const isArray = root instanceof Array;
// you need to fill Arrays with native Array methods (.push())
// and Object with Vue.set()
Object.keys(object).forEach((key, i) => {
if (object[key] instanceof Array) {
createReactiveArray(isArray, root, key, object[key])
} else if (object[key] instanceof Object) {
createReactiveObject(isArray, root, key, object[key]);
} else {
setReactiveValue(isArray, root, key, object[key])
}
})
}
function createReactiveArray(isArray, root, key, values) {
if (isArray) {
root.push([]);
} else {
Vue.set(root, key, []);
}
fillArray(root[key], values)
}
function fillArray(rootArray, arrayElements) {
arrayElements.forEach((element, i) => {
if (element instanceof Array) {
rootArray.push([])
} else if (element instanceof Object) {
rootArray.push({});
} else {
rootArray.push(element);
}
createReactiveNestedFilterObject(rootArray[i], element);
})
}
function createReactiveObject(isArray, obj, key, values) {
if (isArray) {
obj.push({});
} else {
Vue.set(obj, key, {});
}
createReactiveNestedFilterObject(obj[key], values);
}
function setValue(isArray, obj, key, value) {
if (isArray) {
obj.push(value);
} else {
Vue.set(obj, key, value);
}
}
The way I use the above posted solution is like this:
// in store/actions.js
export const actions = {
...
async prepareReactiveObject({ commit }, rawObject) {
commit('CREATE_REACTIVE_OBJECT', rawObject);
},
...
}
// in store/mutations.js
import { helper } from './helpers';
export const mutations = {
...
CREATE_REACTIVE_OBJECT(state, rawObject) {
helper.createReactiveNestedObject(state.rootProperty, rawObject);
},
...
}
// in store/helper.js
// the above functions and
export const helper = {
createReactiveNestedObject
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With