On pushing object in array the vue ui is not getting updated - javascript

I am stucked in a problem where I have created a custom drag drop component which is working fine,
there is an array which is connected to vuex store, i have created the mutations to update or add new object inside the array that array is getting changed after drop operation but the ui is not getting changed
I am fetching that array using this.$store.getters['moduleName/functionName']
mutation.js=>
export default {
setNewValue(state, payload){
state.arr.push(payload);
}
}
handleDrop function =>
handleDrop(event, data){
let actionType = this.$store.getters["moduleName/getActionType"];
let obj;
let length = this.$store.getters["moduleName/getLengthOfArr"];
obj = {
id: length == 0 ? 1 : length + 1,
name: data.name,
isConditional: false,
...actionType[data.type],
yes: [],
no: [],
};
this.$store.commit("moduleName/setNewValue", obj);
action Type are some objects which i need to add based on data type provided
i guess it is due to reactivity of object properties or due to computed property which i am using in ui
computed : {
arr(){
return this.$store.getters['moduleName/getArray'];
}
}
getters.js
export default {
getArray(state){
return state.arr;
}
}
Thanks in advance

After some debugging i solved the problem as the problem was with rendering the data was getting updated and even store was getting updated
while rendering i was using the same object which was rendered previously but that made me so much confused due to vuejs' reactivity with computed property.

Related

React JS Updating item in State object to take effect immediately

React JS class component
I know there have been many posts on this subject but I can't seem to get this scenario to work.
Basically on my HandleClickSave event I want to update an item in my object in state without affecting the other values and then passing this updated oblect onto my service to get updated in the db.
The 'item' in question is the 'design' from the (unLayer) React-email-editor.
Problem is after the service is run in 'HandleClickSave' point 3 below, the receiving field 'DesignStructure' in the db is NULL every time. The other two fields are fine as these are saved to state object elsewhere.
Part of the problem is that the Email-Editor doesn't have an 'onChange' property which is where I would normally update the state. The other two values in the object are input texts and they do have an onChange which is how their state counterparts are updated.
This is the object 'NewsletterDesign':
{
"DesignId": "1",
"DesignName": "DesignLayout 1 Test",
"DesignStructure": null
}
In my React class component...
this.state = {
NewsletterDesign: {}
}
And the HandleClickSave event....
HandleClickSave () {
const { NewsletterDesign } = this.state
this.editor.saveDesign((design) => {
this.setState(prevState => ({
NewsletterDesign: {
...prevState.NewsletterDesign,
DesignStructure: design
}
}));
// Update to database passing in the object 'NewsletterDesign'. Field 'DesignStructure' in db is null every time, but other two fields are updated.
NewsletterService.UpdateCreateNewsletterDesign(NewsletterDesign)
etc....etc..
React's setState is not update immediately. read more here.
You can simply do it inside setState by
this.setState(prevState => {
const newState = {
NewsletterDesign: {
...prevState.NewsletterDesign,
DesignStructure: design
}
};
NewsletterService.UpdateCreateNewsletterDesign(newState.NewsletterDesign);
return newState;
});
The setState is an async operation. Meaning, that it's not guaranteed that the new state that you have updated will be accessible just after the state is updated. You can read more here
So in such cases, one of the way is to do the required operation first and then use the result at multiple places.
HandleClickSave () {
const { NewsletterDesign } = this.state
this.editor.saveDesign((design) => {
let newNewsletterDesign = { ...NewsletterDesign,
DesignStructure: design
};
this.setState(newNewsletterDesign);
NewsletterService.UpdateCreateNewsletterDesign(newNewsletterDesign)

Vuex: Can't change deeply nested state data inside actions

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
);
Edit:
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.
As mentioned in comments - it quickly gets complicated if you hold deeply nested state in your store.
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.
It's not pretty, but it works:
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);
}
}
If someone has a smarter way to do this I am very keen to hear it!
Edit:
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
}
Excluding the good practices on the comments.
That you need is: to instruct Vue when the object change (Complex objects are not reactive). Use Vue.set. You need to set the entire object:
Vue.set(
state,
"fullReport",
state.fullReport
);
Documentation: https://v2.vuejs.org/v2/api/#Vue-set

Unable to update the Vuex store and retrieve array

I'm dispatching an array of objects to the Veux store and trying to get the data using "getters" from Vuex. It returns undefined. It is however working for a boolean variable in the same Vuex. For the array, it is not giving a proper result
I am using Vuex to update an array of objects across vue components.
I am dispatching the array of objects
committing the array through "actions"
setting and updating the value through "mutations"
Declaring the array through "state"
finally, from the Vue component, accessing the array through "getters"
I expect Vuex to return the array of objects when I access using "getters"
The store code is as given below
export default new Vuex.Store({
state: {
loading: false,
compoData: []
},
mutations: {
setLoading(state, payload) {
state.loading = payload
},
setCompoData(state, payload) {
state.compoData = payload.data
}
},
actions: {
setLoading({
commit
}, payload) {
commit('setLoading', payload)
},
setCompoData({
commit
}, payload) {
commit('setCompoData', payload)
},
},
getters: {
loading(state) {
return state.loading
},
compoaData(state){
return state.compoData
}
}
})
Vue component code is as given below. I am dispatching the array of objects "compoData" from the component to the store
someFunction(){
.....
some code
.....
this.$store.dispatch('setLoading',false)
this.$store.dispatch('setCompoData',{data:this.compoData})
},
Getting the values from store
gettingCompoData(){
console.log(this.$store.getters.compoData)
},
Though it works fine with the computed variable "loading", it does not work with the "compoData" array of objects .
I tested it with the below function
test(){
console.log(this.compoData, this.loading);
this.$store.dispatch('setCompoData',{data:this.compoData})
console.log(this.$store.getters.compoData, this.$store.getters.loading)
},
And this is the screenshot of the result I got this log result from the "test" function
It is clear that I am not sending an undefined array. But what I am getting from the store getters is undefined. Strangely, it is working for the boolean variable "loading" and it does return a valid result. I'm baffled as to why it does not return the array of objects. please help me out...
Any help would be appreciated. Thank you
you have a typo.
in store you defined a getter compoaData and when getting values from store you're looking for compoData

Vue Component Props are not being updated

In my data I have an object program. I load a list of objects asynchronously and add it to program.sections.
async loadSections() {
if (this.user == null && this.program.sections) { return }
await this.$store.dispatch('loadProgramSections', {program: this.program, user: this.user});
console.log('loaded Sections', this.program);
// this.$set(this.program, 'sections', this.progrmm.sections)
// this.$nextTick(() => { this.$forceUpdate(); })
},
My UI looks like this:
<Section :program="program" ></Section>
So I pass my program object down to my Sections component
In my console log I can see that the field programm.sections is indeed my array of objects but my UI does not get changed. When I put my UI code from the Sections component directly in to my page, it gets updated and the data is dispalyed correctly but when I use the component the props are not being updated correctly.
I already tried the commented lines in my code but it doesn't work.
Any Ideas?
Assign a new object reference in this.program:
async loadSections() {
if (this.user == null && this.program.sections) { return }
await this.$store.dispatch('loadProgramSections', {program: this.program, user: this.user});
console.log('loaded Sections', this.program);
// assign a new object reference
this.program = Object.asssign({}, this.program);
},
Also make sure you initialize program in data() section.
The main issue is that Vue cannot detect the changes in some situations.
e.g. set a item to an array or set an additional property to an object
That's the crux of the problem. You might need Caveats.

vue computed property not able to get data

I am currently experiencing an issue where the computed() property is not able to get data. Although data was already initiated in created() property. Am I doing it wrong? Please advise how I can fix this issue.
const randomPlayers = {
template:
`
<input type="text" v-model="search_player">
<div v-for="player in modPlayers" v-if="list_of_random_players!=null">
<p>{{player.firstname}}</p>
<p>{{player.lastname}}</p>
<div>
`,
props: ['data'],
data (){
return{
list_of_random_players: null,
search_player: null
}
},
created(){
this.get_random_players()
},
computed: {
modPlayers(){
return this.list_of_random_players.filter( person => {
return !this.search_player ||
( person.firstname.toLowerCase().indexOf(this.search_player.toLowerCase()) > -1 || person.lastname.toLowerCase().indexOf(this.search_player.toLowerCase()) > -1)
})
}
},
methods: {
get_random_players: function(){
$.post(
url:'random_url'
data: {
players: data
}
).done((success)=>{
this.list_of_random_players: JSON.parse(success)
})fail((err)=>{
console.log(err)
})
}
}
}
I get the following two errors:
(1) TypeError: Cannot read property 'filter' of null
(2) TypeError: this.list_of_random_players.filter is not a function
From Vue: "When a Vue instance is created, it adds all the properties found in its data object to Vue’s reactivity system. When the values of those properties change, the view will “react”, updating to match the new values."
So data is a function that returns an object but as mentioned by #Sovalina you are not returning it so you cannot access its properties. You need to add return and change null to []:
data() {
return {
list_of_random_players: [],
search_player: []
}
},
or you can do without return and like a regular object:
data: {
list_of_random_players: [],
search_player: []
}
When your Vue component is used multiple times, it is better to use it like a function(first case).
"When defining a component, data must be declared as a function that returns the initial data object. Why? Because there will be many instances created using the same definition. If we still use a plain object for data, that same object will be shared by reference across all instance created! By providing a data function, every time a new instance is created we can call it to return a fresh copy of the initial data."
Reference:link
It might be just a typo but you need to add : to methods as well.

Categories

Resources