How to clear array which is inside nested state? - javascript

I have crated state like below and at some point I want to clear this array and update with new one. I used below code for clearing, but when I check state through react-developer tool, array is still there nothing is happening. Please help me as my newbie to ReactJS.
state = {
formData:{
level:{
element:'select',
value:'Bachelors',
label:true,
labelText:'Level',
config: {
name:'department',
options: [
{val :"Bachelors", text:'Bachelors'},
{val:"Masters", text:'Masters'}
]
},
validation: {
required:false,
}
,
valid:true,
touched:false,
validationText:'',
},
Now I want to clear options array by:
let {options} = this.state.formData.level.config
options = []
this.setState({
options:options
})
But my array is not clearing. How can I achive that?

Two things that you should know.
1) When you do this.setState({ options: options }), you are not updating the options array belonging to the formData object. You are creating a brand new key-value pair in your state called options. If you try to print your updated state, you will find that your state now looks like:
state = { formData: {...}, options: [] }
2) You are trying to directly mutate state, (changing a value belonging to the existing object). This is against React principles, because in order for the component to re-render correctly without being prone to side-effects, you need to provide a brand new state object.
Since its so deeply nested, the likely best option is to deep clone your state-object like this:
const newFormData = JSON.parse(JSON.stringify(this.state.formData))
newFormData.level.config.options = []
this.setState({
formData: newFormData
})
This will probably be the safest way to clear your form data object. Here's a working sandbox with this feature as well. Please use it for reference:
https://codesandbox.io/s/naughty-cache-0ncks

create newstate and change properties then apply the new state to setState() function like this:
changestate = ()=>{
var newstate = this.state;
newstate.formData.level.config.options=["hello","wow"]
this.setState(newstate)
}

You are not clearing the options state but creating a new state with an empty array.
You can either clone the whole object using JSON stringify and JSON parse too . Other way to update nested states without mutation is by using spread operators. The syntax looks a bit complex though as spread nested objects while preserving everything is tricky.
const newFormData = {
...this.state.formData,
level: {
...this.state.formData.level,
config: {
...this.state.formData.level.config,
options: []
}
}
};
this.setState({
formData: newFormData
})

Related

Right way to update the state with arrays or object in React JS

I am trying to update the array values from a callback function to the state value. I need to set the setState inside callback function.
The callback function will have the array of items. I am using class component to achieve this. On a click of that callback function I need to set the state with the array of values in setState.
I have achieved the same by using two ways but I am worried about the performance of the component and I see in some post that using the spread operator will not be ideal solution, instead need to use the push. I am confused which is the best method when the component grows.
I have added the code structure below. Can anybody can assist me what is the best way to update the state. Thanks in advance!
//My Component
class myComp extends React.Component {
constructor(props); {
this.state = {
processingItems: []
}
//1st approach
handleOnAddingItem(processingItem) => {
this.setState({
processingItems: [...processingItem]
})
}
//2nd Approach
items = this.state.processingItems;
items.push(processingItem)
this.setState({
processingItems: items
})
//3rd Approach
handleOnAddingItem(processingItem) => {
this.setState({ ...processingItem
})
}
React new docs suggests spread operator and avoid push pop operations
this.setState( // Replace the state
processingItems: [ // with a new array
...processingItems, // that contains all the old items
{ id: nextId++, ...newItem } // and one new item at the end
]
);
and copy the objects in new state

Copy an object array to use as state - React.js

I'm having trouble understanding why, when there are objects inside the vector, it doesn't create new references to them when copying the vector.
But here's the problem.
const USERS_TYPE = [
{name:"User",icon:faTruck,
inputs:[
{label:"Name",required:true,width:"200px",value:"", error:false},
{label:"ID",required:true,width:"150px",value:"", error:false},
{label:"Email",required:false,width:"150px",value:"", error:false},
]}]
I tried to pass this vector to a state in two ways.
const [users,setUsers] = useState(USERS_TYPE.map(item=>({...item})))
const [users,setUsers] = useState([...USERS_TYPE])
And in both situations changing user with setUser will change USERS_TYPE.
One of the ways I change.
const changes = [...users]
const err = validation(changes[selected-1].inputs)
err.map((index)=>{
changes[selected-1].inputs[index].error = true
})
setUsers(changes)
What solutions could I come up with, change from vector to object, another copy mechanism.
This copy doesn't make much sense as the internal object references remain intact.
Edit: Another important detail is that the USER_TYPE is outside the function component.
it doesn't create new references to them when copying the vector
Because that's not the way JS works. It doesn't deep clone stuff automagically.
const user1 = {id:1}
const user2 = {id:2}
const users = [user1,user2];
const newUsers = [...users]; // this clones users, BUT NOT the objects it contains
console.log(newUsers === users); // false, it's a new array
console.log(newUsers[0] === users[0]) // true, it's the same reference
Ultimately, you are just mutating state. First and most golden rule of react: don't mutate state.
This is the line causing the error:
err.map((index)=>{
// you are mutating an object by doing this
// yes, `changes` is a new array, but you are still mutating the object that is part of state that is nested inside of that array
changes[selected-1].inputs[index].error = true
})
Maybe this would work:
const idx = selected-1;
const err = validation(users[idx].inputs)
setUsers(users => users.map((user,i) => {
if(i !== idx) return user; // you aren't interested in this user, leave it unmodified
// you need to change the inputs for this user
// first, shallow clone the object using the spread operator
return {
...user,
// now inputs must be a new reference as well, so clone it using map
inputs: user.inputs.map((input,index) => ({
// careful not to mutate the input object, clone it using spread
...input,
// and set the error property on the cloned object
error: !!err[index]
}))
}
}))
EDIT: Sorry for all the code edits, I had a bunch of syntax errors. The ultimate point I was trying to get across remained consistent.
EDIT #2:
Another important detail is that the USER_TYPE is outside the function component.
That doesn't really matter in this case as it serves as your initial state. Every time you update state you need to do it immutably (as I've shown you above) so as not to mutate this global object. If you actually mutate it, you'll see that by unmounting the component and re-mounting the component will result in what looks like "retained state" - but it's just that you mutated the global object that served as the template for initial state.

Angular ngrx: TypeError: Cannot freeze array buffer views with elements

Im encountering an issue with ngrx. I have an array in my state to which i want to append objects. This actually works fine if i console.log them i can see that values in my store. But the redux dev tools and the console throw the error "TypeError: Cannot freeze array buffer views with elements".
I have a state that looks like this:
const state: State = {
array: []
};
The Object i pass to my actions looks similar to this:
const obj = {attr: number, data: ImageData};
Where ImageData comes from a Canvas and is extracted with canvas.getContext("2d").getImageData(...);. It should be noted that this Object is huge with over 69000 keys with values from 0 to 255(RGBA).
And i append the new Object to the state/store like this:
createReducer(
initialState,
on(action, (state: State, action)=> {
return {
...state,
array: [...state.array, action.payload]
}
})
);
Furthermore i read that i should deepCopy Objects before passing them to the action so i did that with lodashs copyDeep(), but i got the same results.
Any help is appreciated.
You need state.array in your reducer.
return {
...state,
array: [...state.array, action.payload]
}
I had the same problem, I solved it by cloning the data that I pass to the dispatch method
ex:
const dataCloned = CloneDataInDeep.clone(dataToAdd);
this.store$.dispatch(actionToDispatch({ dataCloned}));
Another option that I have tried is to change this attribute value "strictActionImmutability" in the ngrx configuration from true to false
runtimeChecks: {
strictActionImmutability: false,
....
}
I use Angular and came across that error.
For me, deep copying the array before the dispatch worked
const copyArray = JSON.parse(JSON.stringify(array));
this.store.dispatch(actionDispatch.requestSth({ myArray: copyArray }));
Not the most beautiful solution, I know.
Edit:
Unfortunately the solution was not helpful because the objects inside the array lose the functions when strigified... So back to square one
You can use this code. This will copy the entire object so the dependency will be removed.
const newlyCreatedArray = Object.assign({}, array);
For more details: Object.assign()

React - Change in one object reflects on another even though its not modified

I am trying to change the value for an Object reflects on another if I assign the same object to two state objects. For example service returns the data like below
formData = {
'name':'name',
'data':{
'value':'value',
'isactive':false
}
};
I am trying to assign the same object to two different state objects
this.setState({
formdata: formdata,
initialFormData : formdata
});
If I modify anything on formdata reflects on initialFormData.
I mapped formdata with form. If I modify name it should reflect only on formdata but it also modifies initialFormData but I dont want my initialFormData not to be changed bec I need to reset the whole data with the initialFormData if any one clicks on reset button.
How to achieve the same . Please help me on this . Thanks in advance
If you need more info comment below . I can provide the same.
Initial setState (data from service) saves references to one (base) object. This way both values are pointers to the same data. This can be done that way (for initial render) - problem is with field handlers, mutating state methods.
Your handler by accessing this.state.formdata gets pointer to base object. Mutating this changes this object, not (as you want) value (object) stored in this.state.formdata.
let formdata = this.state.formdata; // OLD REFERENCE
formdata[field] = evt.value; // THIS WAY YOU"RE MUTATING BASE OBJECT
this.setState({ formdata: formdata }); // STILL THE SAME REFERENCE
You need to prepare a new object (deep clone) and use it in setState()
let newObj = JSON.parse(JSON.stringify( this.state.formdata ));
newObj[field] = evt.value;
this.setState({ formdata: newObj });
Deep clone is needed as your base object formData contains data object. Usual methods (assign, spread) are doing shallow copy - they will copy data reference. This way f.e. mutating data.isActive will still affect both values in state.
Read article about differences.
I think you want like this:
// outside the class
const initialFormData = {
// your initial data
}
// inside the class
state = {
formData: initialFormData
}
// when you want to update with initial data
this.setState({
formData: initialFormData
})
This works. You want the form data object copied for both items in state. So this will work fine.
let formData = {
'name':'name',
'data':{
'value':'value',
'isactive':false
}
};
let State = {
initialFormData:Object.create(formData),
formDat:Object.create(formData)
};
console.log(State)

Vue.js - Add or set new data to array in store

I have two Vue components that use a common array set in a store like this:
var store = {
state: {
myArray: []
}
};
var FirstComp = Vue.extend({
template: '#first-template',
data: function () {
return {
arrayData: store.state.myArray
};
}
});
/* A second component quite identical */
I followed the example given in the Vue js guide.
I'm trying to update the data in the array in the store with new data from another array (after an ajax call), so that it impacts both components. I would like to have a nice way of replacing / concating the old array with a new one. I know I can't just replace the array like this store.state.myArray = newArrayData;because I would loose the Vue binding. But the method given in the docs (at least for concat) doesn't work in the case of the store (or maybe I'm missing something?).
Right now, the only way I've found is to use a foreach with push, $removeor $set depending on the operation and it is not that elegant and practical.
For example, for concat, I do this:
$.each(newArray, function (i, val) {
store.state.myArray.push(val);
});
But for replacing it gets uglier. What would be the proper way to this?
(For info, I'm not using Vuex for state management and I don't plan to at the moment, I'm keeping it very simple)
To make the assignment work you can just use "state" in your component like this:
var FirstComp = Vue.extend({
template: '#first-template',
data: function () {
return {
state: store.state
};
}
});
And then use state.myArray. This way if you will do store.state.myArray = newValue it won't break the binding.

Categories

Resources