How can I push to an array (in Vue 3)? - vue.js

I have an array and I would like to push some data to it.
this.myArray.push(['a' => 'b']);
Unfortunately this does not work with Vue 3 as myArray appears to be wrapped in a proxy object.
Does anyone know how to push to an array in Vue 3?

It depends how you define the array
Using ref:
const myArray = ref([1,2,3]);
myArray.value.push(4); // With a `ref` you need to use `value`
Using reactive:
const myArray = reactive([1,2,3])
myArray.push(4); // push works as normal
Other issues:
Your syntax is incorrect for what's being pushed, maybe you want:
myArray.push({ a: 'b' });
Also, keep in mind there is no component this access in the Vue 3 composition API.

Related

How to alter proxy objects in Vuex / Vue3

I'm trying to update the vuex store but it looks like vuex/vue 3 has converted the state to Proxy objects. How to insert new item in Proxy Objects?
i was trying to add item like normal objects like this
addUnitToCurrentOwner(state, unit) {
const { data } = unit.response;
const newOwnedUnit = [data,
...state.currentOwner.owned_units];
state.currentOwner.owned_units = newOwnedUnit;
},
but it looks like the existing objects are in Proxy?
screenshot of console log here ->
how to I add new item/s to proxy objects?
This is not how Vue works. The thing that makes Vue reactive, are Proxies. That's why all the objects are wrapped in Proxies. If you want to change a value in the vuex store then please follow the official documentation, and use Mutations.

Weird Vuejs props behavior with arrays

I created a form component where i pass an object prop to it containing strings and an object array. I then i transfer the elements of the props to the component variables so i can edit them without interacting with the props directly like so:
beforeMount(){
this.var1 = this.props.var1
this.var2 = this.props.var2
this.array = this.props.array
}
when i edit all the other variables, close my component and open it again everything resets BESIDES the array. every time i try to interact with it the reset never happens. i know i am not interreacting directly with the props either in any part of my code so i don't really why this happens when when everything works fine. To fix that i had to do the following:
beforeMount(){
this.var1 = this.props.var1
this.var2 = this.props.var2
this.props.array.forEach(element => {
this.array.push(element)
})
}
why does this work exactly?
I have also been having similar problems with method specific variables not resetting after the function is already over so i find these behaviors a little weird. I am also using "v-if" and also tried to use component keys to reset the component but it doesn't work for the array for whatever reason.
try to copy your array like that :
this.array = [...this.props.array]
I'm not sure about it, but as you pass it within an object, you might actually use you array as reference and not values as you 'd like to.

Vuejs - add property to each item in an array with reactivity

I have banged my head against all kinds of walls for days and would love some help with this please.
I am getting the following error but can't really see how what I'm doing has anything to do with vuex here:
[vuex] do not mutate vuex store state outside mutation handlers.
In my vue component I define an empty array for future data I get from an external API.
data() {
return {
costCentres: [],
};
},
I have a watch on my vuex store object named schedule (which I've brought in using MapState... and also tried with a getter using MapGetter to see if that made any difference). In this watch I create my array costCentres, with each element consisting of about five properties from the API. I add two properties at this point (sections and tasks) which I intend to later populate, and which I need to be reactive so I do so in accordance with the Vue reactivity documentation which all the other questions I've found remotely like mine seem to reference.
watch: {
schedule() {
if (this.schedule.rows) {
this.costCentres = this.schedule.rows.filter((row) => {
return row.cells[
this.schedule.columnKeysByName["Cost Code"]
].value; // returns row if Cost Code value exists
});
this.costCentres.forEach((costCentre) => {
this.$set(costCentre, 'section', null);
this.$set(costCentre, 'task', null);
});
}
},
The this.$set lines throw the earlier mentioned error for every element in the array.
When I later update the properties, the change is reactive so its just the flood of error messages that's got me beat. Obviously if I don't use set then I don't get reactivity.
I have no idea how what I am doing is related to the vuex store as costCentre is a plain old data property.
I've tried hundreds of variations to get this all work (including this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 }) which doesn't seem to work) and I've run out of options so any assistance would be greatly appreciated!
(Please also let me know if I need to show more code - I was trying to keep this concise!)
this.schedule.rows is an array containing some objects (mapped from Vuex so the array and objects inside "belongs" to Vuex)
You are creating this.costCentres by filter - so in the end this.costCentres is just another array containing subset of objects from this.schedule.rows (elements inside are just pointers to objects inside Vuex)
In the forEach loop, you are modifying objects which are part of the Vuex store and as a result getting error [vuex] do not mutate vuex store state outside mutation handlers.
If you want to modify those objects, only way is to use Vuex mutations
Alternative solution is to make a copy of objects (create new objects with same values):
this.costCentres = this.schedule.rows.filter((row) => {
return row.cells[this.schedule.columnKeysByName["Cost Code"]].value;
})
.map((row) => ({...row, section: null, task: null }))
Note: code above creates just a shallow copy so if your objects does contain some deeply nested properties, you have to use some other way to clone them
Now objects inside this.costCentres are not part of the Vuex and can be modified freely without using mutations...

Vue.set() and push() for Vuex store

I have a mutator that attempts to make the follow update:
state.forms[1].data.metrics.push(newArrayItem)
forms is an object with 1 as a key
metrics is an array
For some reason, Vuex successfully updates, but components don't react to this change.
I was reading about Vue.set() on https://v2.vuejs.org/v2/guide/list.html#Object-Change-Detection-Caveats
But I'm not sure how to apply it, or even if it's the right answer at all here.
Thanks for the help.
this.$forceUpdate is working, which is a bit strange because the component loading the data uses computed properties.
Form state is setup initially like so:
const state = {
forms: {}
};
New forms are pushed like so:
state.forms[id] = { formName: payload.formName, data: {metrics: []}};
And new metrics are added like so:
var result = getParent(state, payload.path);
result.metricParent.data.metrics.push({ id: newMetricId(state, result.formId), ...payload.metric });
Your problem is not with pushing to arrays. Your problem is in this line:
state.forms[id] = { formName: payload.formName, data: {metrics: []}};
Because you are creating a new property of state.forms without using Vue.set(), the form is not reactive, so when you push to the metrics array later, it's not recognized.
Instead, you should do this:
Vue.set(state.forms, id, { formName: payload.formName, data: {metrics: []}});
Then, pushing to state.forms[id].data.metrics should work as expected.
Vue setup reactive data looking for how the state/data is setup, by example if in a regular component you define the data like {x: {y: 10}} and you change the data somehow this.x.y = 20; it’s going to work, because Vue make the object with that structure reactive (because is the setup structure) based on that if you try to do, this.x.z = 10; not works because “z” not exists, and you need to tell to Vue that you need to make it reactive, this is when this.$set(this.x, “z”, 10); enters, it’s basically saying “make this data reference in position ‘z’ reactive”, after this point direct calls to this.x.z = ? works, in vuex the same happens, use Vue.set(state.forms, 1, { formName: payload.formName, data: {metrics: []}}); after that the reference to state.forms[1] (including sub data) is now reactive!

Dynamically creating a reactive array in the Vuex's state

My component would like to add a new reactive-array field to the SST (vuex). I tried in beforeCreate hook, but the added array is not reactive; it's just a plain JS array.
Note that this is not the same as adding/removing elements from an existing array created at the Vue's initialization time. Such arrays are "wrapped" and become reactive as expected, mindful of "Array Change Detection" gotchas.
In my case, I'm trying to dynamically add an entirely new field of array type to the SST and make it reactive at the same time. Possible?
Have a look at Reactivity in Depth - Change Detection Caveats:
Change Detection Caveats
Due to the limitations of modern JavaScript, Vue cannot detect property
addition or deletion.
Since Vue performs the getter/setter conversion process during
instance initialization, a property must be present in the data object
in order for Vue to convert it and make it reactive.
But you say you are adding an array dynamically:
I'm trying to dynamically add an entirely new field of array type to the SST and make it reactive at the same time. Possible?
From the docs (bold is mine):
Vue does not allow dynamically adding new root-level reactive properties to an already created instance. However, it’s possible to add reactive properties to a nested object using the Vue.set(object, key, value) method:
Vue.set(vm.someObject, 'myArrayName', [1,2,3]);
Which should help you making your array reactive.
I see two problems here:
add dynamically array using vuex.
add dynamically element to this array and render this element.
I've initiate array if not exist in add method because when I'm receiving data from server myArray is not exist.
My solutuion below:
myVuexArray.js
import Vue from 'vue'
const state = {
myObject: {
myArray: [],
}
}
const getters = {
getMyArray: state => {
return state.myObject.myArray;
}
}
const mutations = {
addElementToArray(state, value) {
if (state.myObject.myArray === null || state.myObject.myArray === undefined || state.myObject.myArray === '') {
// initiate array
state.myObject.myArray = [];
}
// add new element to array
Vue.set(
state.myObject.myArray,
state.myObject.myArray.length,
value
);
// creates a new array everytime this solves the reactivity issue
Vue.set(state, 'myObject.myArray', state.myObject.myArray);
return state.myObject.myArray;
},
removeElementFromArray(state, index) {
state.myObject.myArray.splice(index, 1);
}
}
export default {
state,
mutations,
getters
}
Best regards
Dynamic module registration could help you to achieve this :
https://vuex.vuejs.org/en/modules.html
This would allow you to dynamically register a new module containing your array field in the beforeCreate hook.