I have an app where the redux state has a field called data that's null initially, then when uploading something it changes to an array of objects and the reducer uses delete to remove an object when we press the required button. But that doesn't change the length of the array so it causes an uncaught error. The simplest solution would be to simply change the length of the array after we delete that object. Can this produce any bugs?
EDIT: This is the code:
case actions.RIR:
const filtered = [...state.data];
delete filtered[action.payload];
filtered.length--; // this has been added by me
return {...state, data: [...filtered]};
The original code isn't mine. It's part of a project. I've been asked to fix an error.
First one foremost, reducers should be pure - meaning - no side effects.
delete is a side effect since it mutates an original object.
In reducers, use immutable patterns - either use filter, or splice a copy of the original array (less recomended).
Besides that, delete will simply make this element undefined, and your array will look like:
['item 1','item 2'....,empty,'item n']
I would write it:
case actions.RIR:
const filtered = state.data.filter(item => item !== action.payload);
return {...state, data: filtered};
Or whatever filter logic you might require.
Related
I'm using a useEffect hook to trigger a setState update. I'm getting some strange and inconsistent behaviour from the previous param:
useEffect(() => {
setCurrentPicturesObject((existing) => {
const clone = {...existing}
console.log({
existing,
existingdotNocolor: existing.nocolor,
selectedColorState,
selectedColorArray: existing["nocolor"],
clone
});
return existing
});
}, [selectedColorState]);
So you'd expect that the clone of the object would return an object with the same keys and values, right? Not here:
Somehow existing goes from being an object with a nocolor prop with an array of two strings, to switching to an array with one string. Similarly when I try to access the nocolor prop it only returns an array with one string.
I can't understand. existing changes as soon as I try to access it with anything other than simply console logging it directly?
Not entirely sure on the details, but essentially it seems that the console is logging a live view of the object, which is a static string. More info in this answer.
I am working on an Angular 9, RxJS 6 app and have a question regarding the different outcomes of piping subject values and doing unit conversion in that pipe.
Please have a look at this stackblitz. There, inside the backend.service.ts file, an observable is created that does some "unit conversion" and returns everything that is emmitted to the _commodities Subject. If you look at the convertCommodityUnits function, please notice that I commented out the working example and instead have the way I solved it initially.
My question: When you use the unsubscribe buttons on the screen and subscribe again, when using the "conversion solution" that just overrides the object without making a copy, the values in the HTML are converted multiple times, so the pipe does not use the original data that the subject provides. If you use the other code, so creating a clone of the commodity object inside convertCommodityUnits, it works like expected.
Now, I don't understand why the two ways of converting the data behave so differently. I get that one manipulates the data directly, because js does Call by sharing and one returns a new object. But the object that is passed to the convertCommodityUnits function is created by the array.prototype.map function, so it should not overwrite anything, right? I expect that RxJS uses the original, last data that was emitted to the subject to pass into the pipe/map operators, but that does not seem to be the case in the example, right?
How/Why are the values converted multiple times here?
This is more or less a follow-up question on this: Angular/RxJS update piped subject manually (even if no data changed), "unit conversion in rxjs pipe", so it's the same setup.
When you're using map you got a new reference for the array. But you don't get new objects in the newly generated array (shallow copy of the array), so you're mutating the data inside the element.
In the destructuring solution, because you have only primitive types in each object in the array, you kind of generate completely brand new elements to your array each time the conversion method is called (this is important: not only a new array but also new elements in the array => you have performed a deep copy of the array). So you don't accumulate successively the values in each subscription.
It doesn't mean that the 1-level destructuring solution like you used in the provided stackblitz demo will work in all cases. I've seen this mistake being made a lot out there, particularly in redux pattern frameworks that need you to not mutate the stored data, like ngrx, ngxs etc. If you had complex objects in your array, the 1-level destructuring would've kept untouched all the embedded objects in each element of the array. I think it's easier to describe this behavior with examples:
const obj1 = {a: 1};
const array = [{b: 2, obj: obj1}];
// after every newArray assignment in the below code,
// console.log(newArray === array) prints false to the console
let newArray = [...array];
console.log(array[0] === newArray[0]); // true
newArray = array.map(item => item);
console.log(array[0] === newArray[0]); // true
newArray = array.map(item => ({...item}));
console.log(array[0] === newArray[0]); // false
console.log(array[0].obj === newArray[0].obj); // true
newArray = array.map(item => ({
...item,
obj: {...item.obj}
}));
console.log(array[0] === newArray[0]); // false
console.log(array[0].obj === newArray[0].obj); // false
I've a react application, and i've found a bug where i'm filtering an array for searching the current item the user has selected and then i'm going to do stuff with it...but, didn't know that filter function return a reference to the array's item, so, every change i made to the selected item i'm doing the same to the prop array.
const pippo = maledettoArray.filter(item => item.id === idInfame)[0];
How can i filter the prop array to get the specific item without changing it?
You can use find method instead of filter which returns first match and exits the loop.
const pippo = maledettoArray.find(item => item.id === idInfame)
To create shallow copy of the object you can use Object.assign or spread syntax.
const clone = {...pipo}
If you want to create deep copy of the nested object then you could use Lodash _.cloneDeep(value) method.
First of all, I would recommend you use the find function instead of filter to avoid the empty array return and undefined at [0]
Secondly, yes, the object reference would be returned. To avoid this, you can use Object.assign({}, originalObject) or using the spread syntax {...originalObject} . A potential problem would be with nested objects which could still be a problem.
Probably this article can help you in such case https://medium.com/#Farzad_YZ/3-ways-to-clone-objects-in-javascript-f752d148054d
Currently I've a react function that removes from a Array called rents the current rent perfect. The issue is that I need to update the rent row value called status and set property from 1 to 4 the code below works. I don't seem to get how to get the Index of rent to be able to update it.
removeItem (itemIndex) {
this.state.rents.splice(itemIndex, 1) // removes the element
this.setState({rents: this.state.rents}) // sets again the array without the value to the rent prop
console.log(itemIndex) // itemIndex
}
currently I'm adding this to the code to debug but get this error
console.log(this.state.rents[itemIndex].unique_key)
Stack Trace
TypeError: Cannot read property 'unique_key' of undefined
I need to be able to update the rent property value called status from 1 to 4 and setState again
To elaborate the comments, starting first with the most important:
Like #devserkan said, you should never mutate your state (and props), otherwise you start to see some really weird hard-to-make-sense bugs. When manipulating state, always create a copy of it. You can read more here.
Now for your question:
this.setState is asynchronous, so to get your state's updated value you should use a callback function
const rents = [...this.state.rents]; // create a copy
rents.splice(itemIndex, 1);
this.setState({ rents }, () => {
console.log(this.state.rents); // this will be up-to-date
});
console.log(this.state.rents); // this won't
Personally, I like using the filter method to remove items from the state and want to give an alternative solution. As we tried to explain in the comments and #Thiago Loddi's answer, you shouldn't mutate your state like this.
For arrays, use methods like map, filter, slice, concat to create new ones according to the situation. You can also use spread syntax to shallow copy your array. Then set your state using this new one. For objects, you can use Object.assign or spread syntax again to create new ones.
A warning, spread syntax and Object.assign creates shallow copies. If you mutate a nested property of this newly created object, you will mutate the original one. Just keep in mind, for this situation you need a deep copy or you should change the object again without mutating it somehow.
Here is the alternative solution with filter.
removeItem = itemIndex => {
const newRents = this.state.rents.filter((_, index) => index !== itemIndex);
this.setState({ rents: newRents });
};
If you want to log this new state, you can use a callback to setState but personally, I like to log the state in the render method. So here is one more alternative :)
render() {
console.log( this.state.rents );
...
}
So I've got a vue component I'm using as an editing interface...
The database has a jsonified array inside of it (don't ask... I don't like it either), which has to be in a particular format... In order to allow admins to edit this string array without breaking the format, I'm making a vue editing component which will break the parts up into various textboxes etc...
I would like to have two separate variables - one for what the string array IS, and one for what they are changing... the first variable would be updated when they saved their changes...
The issue I have is that for some reason, when I update one of the variables... not only does the other variable change... but the prop changes as well...
I was under the impression that components could not change their props..?
In particular, the array looks something like this:
[
'1',
'2'
['1','2','3','4']
]
And when I do a .splice() on the sub-array, both variables and the prop updates...
Some sample code...
Laravel blade view:
<editor :somearray={{ $someJsonifiedArray }}></editor>
Component props/data setup:
props: {
somearray: {
default: [],
type: Array
}
}
data(){
return {
editedArray: this.somearray, // This is what will be saved
wipArray: this.somearray // This is what changes as they edit
}
}
Some of the methods:
resetChanges(){
this.wipArray = this.editedArray;
}
I'm probably missing something obvious... or misunderstanding how things work...
Javascript Array/Object is passed by reference, not by value!
if you do this
return {
editedArray: this.somearray, // This is what will be saved
wipArray: this.somearray // This is what changes as they edit
}
whenever you edit editedArray or wipArray, you are actually editing somearray, because they all refer back to the same array/Object .
So you gotta clone the array/Object instead of passing it directly. The simplest way to clone object is by using spread operator (or in some cases, deep cloning will be required). The simplest way to clone array is by using slice.
return {
editedArray: {...this.somearray}, // This is what will be saved
wipArray: {...this.somearray} // This is what changes as they edit
}