I read the official Vuex example of how to mutate the state of element in array of objects data.
editTodo (state, { todo, text = todo.text, done = todo.done }) {
const index = state.todos.indexOf(todo)
state.todos.splice(index, 1, {
...todo,
text,
done
})
}
It seems a bit an overkill to find the index first (first loop), perform a splice and perform a copy with spread operator ...todo (would it be considered a second loop?).
Going by the docs:
When adding new properties to an Object, you should either:
Use Vue.set(obj, 'newProp', 123), or Replace that Object with a fresh one. For example, using the object spread syntax: state.obj = { ...state.obj, newProp: 123 }
There are only two ways to set / update properties to object.
In bigger data scenario that is causing some performance issues. Is there any better (more efficient) way to update an element in array of objects?
Related
I am using the hookstate.js library and am trying to something as simple as to get the entire Javascript object from the State object without having to reference the key of the object.
For example, state.set({first:'John',last:'Doe'}) and then somehow access the entire object (as a bona fide JS object, not State) without having to know the object key and reference state.first.get().
Is there no built in way to do such a simple thing?
I can do so with a reduce:
const { keys } = state
const jsObject= keys.reduce((pre, cur) => {
const stateVal = state[cur].get()
return { ...pre, [cur]: stateVal }
}, {})
However, this is not possible if I am expecting nested objects.
Author of Hookstate here. You can call State.get() on the root of the state and use noproxy and stealth options (since Hookstate 4 version) for the get method. It will do what you require.
Did you try calling get() on the root of the object?
Like state.get()
I am optimizing performance of a React app by reducing unnecessary re-renders. The component I am working on receives a single prop containing an object with many keys including array of objects. I am using shouldComponentUpdate to check for changes in prop, but it's not working as expected:
shouldComponentUpdate(nextProps) {
if (!(_.isEqual(nextProps, this.props))) {
console.log('difference', _.differenceWith(nextProps, this.props));
return true;
}
return false;
}
isEqual is a method from Lodash for deep comparing objects. differenceWith is a Lodash method for finding difference in two objects. The weird thing is that using isEqual does not reduce re-renderings, and differenceWith prints an empty array []. But if I use JSON.stringify instead of isEqual, re-renders are reduced by half, but differenceWith still prints [].
shouldComponentUpdate(nextProps) {
if ( JSON.stringify(nextProps) !== JSON.stringify(this.props) ) {
console.log('difference', _.differenceWith(nextProps, this.props));
return true;
}
return false;
}
Why is there a difference in behaviour of isEqual and JSON.stringify when they are essentially doing the same thing, although in a different way (note that isEqual is also order sensitive)?
What is the best way to avoid re-renderings here?
1 - Why is there a difference in behaviour of isEqual and JSON.stringify when they are essentially doing the same thing, although in a different way (note that isEqual is also order sensitive)?
Take a look on what I found here.
Well that depends. For JSON.stringify(), the order matters. So if the key-value pair are ordered differently in the two objects but are the same, it will return false. Whereas it doesn't matter in Lodash isEqual, it will return true as along as the key-value pair exists.
const one = {
fruit: '🥝',
energy: '255kJ',
};
const two = {
energy: '255kJ',
fruit: '🥝',
};
// Using JavaScript
JSON.stringify(one) === JSON.stringify(two); // false
// Using Lodash
_.isEqual(one, two); // true
This means that you JSON.stringify can work, but not in all cases, so probably you shouldn't use it for your case.
I also found a benchmark that compares both. JSON.stringify is better for less deep nested objects, but _.isEqual gets better with more deep nested objects.
_.differenceWith is used to compare arrays wich receives 3 parameters, 2 are arrays to compare and the third one is the comparator.
Doing this _.differenceWith(nextProps, this.props) would be the same as _.difference(nextProps, this.props) as you can see in the docs
_.differenceWith
This method is like _.difference except that it accepts comparator which is invoked to compare elements of array to values. The order and references of result values are determined by the first array. The comparator is invoked with two arguments: (arrVal, othVal).
_.difference
Creates an array of array values not included in the other given arrays using SameValueZero for equality comparisons. The order and references of result values are determined by the first array.
So using one of this, will return a new array with the values different values. If it returns [] it means that the array have the same values but it can be in a different other.
_.differenceWith is good if you have an array of objects and you also want to compare the objects. This can be seen in the doc's example.
var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
_.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual);
// => [{ 'x': 2, 'y': 1 }]
2 - What is the best way to avoid re-renderings here?
For your case, I recommend using _.isEqual because it compares exactly the order of an array and you can have objects with the same properties but in different orders, and it will be the same.
Use said
The weird thing is that using isEqual does not reduce re-renderings, and differenceWith prints an empty array []. But if I use JSON.stringify instead of isEqual, re-renders are reduced by half...
And I'm not sure why this happens, it deppends on alot of things, but you should definitely use _.isEqual if you want to compare state or props of a component.
You can also React.memo and React.PureComponent, this might help in some cases.
And as you can see in the docs of memo
If your function component renders the same result given the same props, you can wrap it in a call to React.memo for a performance boost in some cases by memoizing the result. This means that React will skip rendering the component, and reuse the last rendered result.
...
This method only exists as a performance optimization. Do not rely on it to “prevent” a render, as this can lead to bugs
And the docs of PureComponent
React.PureComponent is similar to React.Component. The difference between them is that React.Component doesn’t implement shouldComponentUpdate(), but React.PureComponent implements it with a shallow prop and state comparison.
If your React component’s render() function renders the same result given the same props and state, you can use React.PureComponent for a performance boost in some cases.
I have this small function (within my Angular 7 application) which uses JavaScript reduce(), and locates an object within a nested array of objects. I can then proceed to update certain properties on the fly.
Now, in addition to this find logic, I would like to also insert/delete an object into/from the nested array.
Question is: once I do locate my object, can I push() and/or delete an object ?
const input={UID:2,GUID:"",LocationName:"USA",ParentLocation:null,subs:[{UID:42,GUID:"",LocationName:"New Jersey",Description:"",subs:[{UID:3,GUID:"",LocationName:"Essex County",ParentLocation:null,"subs":[{UID:4,LocationName:"Newark",ParentLocation:3,"subs":[{"UID":49,"GUID":"","LocationName":"Doctor Smith's Office","LocationType":{"UID":2,"LocationTypeName":"Practice","Description":"other location"},"subs":[{"HostID":38,"HostName":"Ocean Host",}]}]}]}]}]};
const findUIDObj = (uid, parent) => {
const { UID, subs } = parent;
if (UID === uid) {
const { subs, ...rest } = parent;
return rest;
}
if (subs) return subs.reduce((found, child) => found || findUIDObj(uid, child), null);
};
console.log(findUIDObj(49, input));
var obj = findUIDObj(49, input);
delete obj;
For example, in my Angular 7 app, it complains if I attempt to delete the found object:
ex/
var obj = findUIDObj(49, input);
delete obj;
'delete' cannot be called on an identifier in strict mode.
Looking briefly at your code, I see you are using a const identifier to declare your data collection. We only use const for static data that does not change, and that’s the purpose of it. So, first of all matters, that seems to be the problem. To test it change it to let. Now, as for methods for data management, immutability is worthy of your consideration for many reasons, but namely Angular will rerender the entire object regardless of altering the existing object, or receiveing a new object. You can look up Immutable JavaScript to understand more. In many cases, creating immutable data management is done with a library, you can do it yourself. Basically, create a function called copy( data ), or something so that you pass in the original object, but you get a copy of it in return with no reference to the original object. That way one does not accidentally change the original object. To do this you can do this inside of your copy function: return JSON.parse(JSON.stringify( data )) ;
The only problem you might run into here is deep nested objects, or objects with circular references can cause problems. I have an overriding stringify method to mange this in little libraries I’ve written.
delete obj would never do what you want: first of all, it is not even an object from your input, since the function created a new object from the found object, excluding the subs property, and returned that. But more importantly, delete is used for deleting properties, not objects.
It seems you want to remove a matching object from its parent subs property. For that you would need to mutate the subs array, so it would exclude the matching object. For that to work in a generic way, your input should be an array. Otherwise that root object could not be removed from anything.
With that in mind, your lookup function should return the array in which the match was found and at which index. With those pieces of information you can decide to remove that element from the array, or to insert another object at that index.
Here is how it could work with removal:
const input=[{UID:2,GUID:"",LocationName:"USA",ParentLocation:null,subs:[{UID:42,GUID:"",LocationName:"New Jersey",Description:"",subs:[{UID:3,GUID:"",LocationName:"Essex County",ParentLocation:null,"subs":[{UID:4,LocationName:"Newark",ParentLocation:3,"subs":[{"UID":49,"GUID":"","LocationName":"Doctor Smith's Office","LocationType":{"UID":2,"LocationTypeName":"Practice","Description":"other location"},"subs":[{"HostID":38,"HostName":"Ocean Host",}]}]}]}]}]}];
const findUIDObj = (uid, arr) => {
if (!arr) return;
const idx = arr.findIndex(obj => obj.UID === uid);
if (idx > -1) return [arr, idx];
for (const obj of arr) {
const result = findUIDObj(uid, obj.subs);
if (result) return result;
}
};
console.log(findUIDObj(49, input));
const [arr, idx] = findUIDObj(49, input) || [];
if (arr) {
arr.splice(idx, 1); // Remove object from its parent array
}
Is there a way I could setState with arrays?
this.state={
numberOne:'',
numberTwo:'',
numberThree:'',
}
this.setState({numberOne:1})
this.setState({numberTwo:2})
this.setState({numberThree:3})
The code above will work but the code below wouldn't.
this.state={
numbers:['','','']
}
this.setState({numbers[0]:0})
this.setState({numbers[1]:1})
this.setState({numbers[1]:1})
How to I setState with arrays?
You can do it as follows:
this.setState({ numbers: [1, 2, 3] })
Or if you want to edit a specific array index, then you have to create a new copy of the array, without mutating the state:
const copied = [...this.state.numbers]
copied[0] = 1
this.setState({ numbers: copied })
Keep in mind that the copying can be done by value or reference, according to the array values types.
For booleans, strings, numbers array values it's is done by value (deep copying) and mutating the copied array values won't affect the origin one's values. So if this is your case - then you should not be worried about.
For objects array values - the copying is done by reference (shallow copying) and you have to be careful to not mutate the original array incidentally. You can read more about the array copying here.
In short: Whatever approach you take, always make sure you don't mutate the origin (state) array.
That syntax makes no sense. And you can't mutate the state directly. For changing individual array elements or object properties, you need to copy that state property first, modify that value, then replace the entire property. E.g:
// Initial state in constructor
this.state = { numbers: [1,2,3] };
Then to change it somewhere:
let newNumbers = this.state.numbers.slice(0); // makes copy of the array
newNumbers[1] = 5;
this.setState({numbers: newNumbers});
and then you state will be { numbers: [1,5,3] }
I have some inherited typescript code that looks like this (inside of a function):
const Statuses = [...this.Statuses]
this.Statuses refers to an array of status codes (defined as Statuses = status[];) defined inside the same .ts file. Subsequent to the line above, the code operates on Statuses
I'm trying to understand what the [...this.var] syntax does? Why not simply refer to this.Statuses in the first place?
Yes, I'm new to javascript/typescript....
Thanks in advance.
It's "spread notation", it spreads out this.Statuses (which must be some kind of Iterable, such as an array) into discrete elements. In this case, those form a new array (because the ...this.Statuses is within [], which creates an array).
So if this.Statuses is an array, it's functionally identical to const Statuses = this.Statuses.slice(); (or also the following).
If this.Statuses is another kind of Iterable, it's functionally identical to const Statuses = Array.from(this.Statuses);.
It's a aesthetically-pleasing way of copying an array, to avoid mutations to the original. It uses the spread syntax, as mentioned in other answers.
const a = [ 1 ]
const b = a
const c = [...a]
b.push(2)
c.push(3)
console.log(` original: ${a}`)
console.log(`reference: ${b}`)
console.log(` copy: ${c}`)
It's spread syntax. It allows to copy items from one array (or object) to another. In your case it's used to make a shallow copy of this.Statuses to avoid original array mutations. Thanks for this when you for example push new item to Statuses the original array (this.Statuses) will remain unchanged.