Unable to handle nested array in react state, here what I am trying I need to push value in this.state.form.contentFormArr
let langulageform, contentObj,contentFormArr;
contentObj={heading:'',subheading:''};
contentFormArr=[this.contentObj]
langulageform =[
{key:"Ar",lang:"Arabic",contentFormArr:this.contentFormArr},
{key:"En",lang:"English",contentFormArr:this.contentFormArr},
{key:"Ru",lang:"Russian",contentFormArr:this.contentFormArr},
{key:"Sp",lang:"Spanish",contentFormArr:this.contentFormArr},
{key:"Ve",lang:"Vetnamese",contentFormArr:this.contentFormArr}
];
constructor(){
super()
this.addContentArea = this.addContentArea.bind(this)
this.state={
form:this.langulageform
}
}
addContentArea(index){
this.setState((state)=>{
const contentformArr = [...state.form[index].contentFormArr,this.contentObj]
return{
...state.form.contentFormArr,
contentformArr
}
})
}
I made multiple forms by the iterating this.state.form array, Now I need when the user clicks on any form box button to add more field then it will push value in particular index array and then it will iterate more fields.
Well I did it but I think the way which I use is not good practice. First I made the correction on Array which I set to state as the initial value, because fore I was set array value to contentForm array when I made changes to them it set changes to everywhere on state Object.
langulageform =[
{key:"Ar",lang:"Arabic",contentFormArr:[{heading:'',subheading:''}]},
{key:"En",lang:"English",contentFormArr:[{heading:'',subheading:''}]},
{key:"Ru",lang:"Russian",contentFormArr:[{heading:'',subheading:''}]},
{key:"Sp",lang:"Spanish",contentFormArr:[{heading:'',subheading:''}]},
{key:"Ve",lang:"Vetnamese",contentFormArr:[{heading:'',subheading:''}]}
];
Secondly I create one blank array and push all values in it, so that I can push value in it, This can't be done in state because of immutable object, and after getting correct index object I made corrections and return the object.
addContentArea(index){
this.setState((state)=>{
let Arr=[];
state.form.map((e,i)=>{
Arr.push(e);
if(i===index){
Arr.contentFormArr.push({})
}
})
return{
Arr
}
})
}
I did it because I feel to deal with multidimensional immutable arrays are bit tricky this is not an optimized way but it works every time and I have checked that the react component lifecycle methods are also working fine with this.
Related
I have the following method compares two array, one coming from the props and the other one from my own component. Every element that exists in my props array but doesnt exist in my components array is inserted in a third array with the added property called "destroy : true" so i can send it to the back end to be deleted from the database.
However for whatever reason my props is being updated instead of the variables i use in the method to do all this. i am not really sure why since i am not referencing the prop directly but i do copy its content to the variables in the method.
updateArray(){
let updatedArray = []
let oldArray = [...this.props.array]
oldArray.forEach(element => {
if(this.componentArray.indexOf(element) > -1){
updatedArray.push(element)
}else{
let newElement = element
newElement.destroy = true
updatedArray.push(newElement)
}
})
return updatedArray
},
why does this happen exactly? every other element in my component works fine except this.
Yes, you are copying the elements of the this.props.array array into a new array local to the method but given that the elements of the array are objects, both arrays are in the end containing same objects (references to the objects)
You can create shallow copy of the original element with the spread operator let newElement = { ...element } - this creates the completely new object and copy all properties of the original object. But be aware that if any property of original objects contains array/object, you have the same problem... just one level down
You can look at the issue via codesandbox
Codesandbox: https://codesandbox.io/s/great-frog-m2s7h
Context
I am trying to fetch some data and populate some variable and render the value in React, essentially following the documentation await/async + runInAction example: https://mobx.js.org/actions.html#asynchronous-actions
However, when the data is fetched, React actually does not re-render. However, if you edit the text in there(i.e. change hi to his or whatever, then you see the correct value appear.
Problem
What exactly am I doing wrong with the data fetching? Why isn't the observable value not being re-rendered correctly when its been assigned a value after some data has been fetched?
This is actually one of the limitations of Mobx.
From the docs:
make(Auto)Observable only supports properties that are already defined. Make sure your compiler configuration is correct, or as workaround, that a value is assigned to all properties before using make(Auto)Observable. Without correct configuration, fields that are declared but not initialized (like in class X { y; }) will not be picked up correctly.
Just initialize the title this.title=undefined and it will work.
Its a simple mistake. MobX can not compare data of title to something that dosent exist. Stuff should have default value of some sort, even a null. hence in a constructor you need to define title default value like
constructor() {
this.title = [],
makeAutoObservable(this);
}
or, if you wish, even null
constructor() {
this.title = null,
makeAutoObservable(this);
}
Basiclaly, whenever you create some observable variable in a store - you need to define its default value in a constructor above makeAutoObservable.
Here, a forked project of yours with juswt this 1 line change to make it work https://codesandbox.io/s/suspicious-noether-fnhjw
I have created the following demo to help me describe my question: https://codesandbox.io/s/dazzling-https-6ztj2
I have a form where I submit information and store it in a database. On another page, I retrieve this data, and set the checked property for the checkbox accordingly. This part works, in the demo this is represented by the dataFromAPI variable.
Now, the problem is that when I'd like to update the checkboxes, I get all sorts of errors and I don't know how to solve this. The ultimate goal is that I modify the form (either uncheck a checked box or vice versa) and send it off to the database - essentially this is an UPDATE operation, but again that's not visible in the demo.
Any suggestions?
Also note that I have simplified the demo, in the real app I'm working on I have multiple form elements and multiple values in the state.
I recommend you to work with an array of all the id's or whatever you want it to be your list's keys and "map" on the array like here https://reactjs.org/docs/lists-and-keys.html.
It also helps you to control each checkbox element as an item.
Neither your add or delete will work as it is.
Array.push returns the length of the new array, not the new array.
Array.splice returns a new array of the deleted items. And it mutates the original which you shouldn't do. We'll use filter instead.
Change your state setter to this:
// Since we are using the updater form of setState now, we need to persist the event.
e.persist();
setQuestion(prev => ({
...prev,
[e.target.name]: prev.topics.includes(e.target.value)
// Return false to remove the part of the array we don't want anymore
? prev.topics.filter((value) => value != e.target.value)
// Create a new array instead of mutating state
: [...prev.topics, e.target.value]
}));
As regard your example in the codesandbox you can get the expected result using the following snippet
//the idea here is if it exists then remove it otherwise add it to the array.
const handleChange = e => {
let x = data.topics.includes(e.target.value) ? data.topics.filter(item => item !== e.target.value): [...data.topics, e.target.value]
setQuestion({topics:x})
};
So you can get the idea and implement it in your actual application.
I noticed the problem with your code was that you changed the nature of question stored in state which makes it difficult to get the attribute topics when next react re-renders Also you were directly mutating the state. its best to alway use functional array manipulating methods are free from side effects like map, filter and reduce where possible.
I want to pass array from one component to the other in vueJs, which i am able to do with
<add-new-admin-modal
:permissions = "permissions"
</add-new-admin-modal>
In my other component which is a modal actually,
I am receiving the props as,
props: {
permissions: {
type: Array,
default: () => []
}
}
Here when i try to change the permissions array, it reflects the parent data, As mentioned in the documentation.
https://v2.vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow
so i tried with spread operator
data () {
return {
statePermissions: [...this.permissions]
}
}
The statePermissions array is still empty when i try above method,
I even tried with Object.assign
data () {
return {
statePermissions: Object.assign([], this.permissions)
}
}
Still it doesn't work.
In my Modal, I am accessing it as
<div v-for = "permission in statePermissions" :key = "permission.id">
...someHtml here.
</div>
The main idea is, I have a component which gets the data through an api, then i have a modal which takes this data and updates it accordingly and submit it to an api.
when the modal is closed, the parent component should need to have the unedited data, so that if modal is reopened it should get unedited data.
In the process of using Modal, My parent component remains in the same state (It neither gets mounted nor changed), so their is no point in making the request for default data again from parent.
Your problem is probably that the default value for you prop is an empty array and you're assigning it to a local variable before the property is properly populated by the parent (or it might even be a lifecycle issue).
Try moving your assignment to the local variable to the mounted() hook or even better if you wan't it to be reactive watch your property:
watch: {
permissions(newValue) {
this.statePermissions = newValue
}
}
You also don't need to ...spread an array to assign it to an array.
Since permissions is an array of objects, when you make a copy of it, the resulting array is a shallow copy meaning it will contain the same references to objects. That's why modifying the new array's values update the old array's values as well. You need to create copies of the objects inside permissions.
If permissions only contains primitives, you can do something like this:
this.statePermissions = JSON.parse(JSON.stringify(this.permissions));
If permissions is of a certain type (i.e. new Permission()), you can map it (I think this is cleaner):
this.statePermissions = this.permissions.map(x => new Permission(x.propA, x.propB, etc.));
This way, each cloned object in statePermissions will have the same properties as the object it's copied from in permissions, but it's independent so modifications won't affect the parent it was created from.
There's a few other ways in this post.
So I have a React component where I have:
getInitialState: function () {
return {
peopleDetails: []
}
}
peopleDetails will contain objects which will have property names like name, email, phone etc.
Here's the problem: I'll be passing methods to child components which should mutate the objects of this array. Some methods will just edit a person's email, another will edit the phone etc. Thus, they'll need to call setState.
As far as I understand, setState should return a new copy of the object. I find this impossible to do because things are difficult enough even if I have to mutate the object (I'll have to specify some filters to find it inside an array, then change its value etc).
I'm pretty sure a lot of React apps use data structures like these...where in the state you have an array holding objects and you'll need to do stuff to those objects. Is there some easy way to accomplish this, respecting the "rule" that each setState invocation should return a completely new state object?
If you really don't want to use immutable data, then you can directly mutate the properties you like and invoke a this.forceUpdate() instead of setState after mutations have occurred.
If you were to proceed with immutable data, then adding to that array would return a new array containing the new object. If you removed that person, you'd then return a new empty array. If you were to modify an item in-place in the array, then you'd build a new array using concat/slice to get all elements that haven't changed, then add your changed element, then add everything after the changed element's index.
For a more in-depth discussion on avoiding array mutations, check out this talk.