This is mutating my redux state somehow. I don't know why:
allProducts is a state in my store with initial_state of [] and gets values from api call.
const [products, setProducts] = useState([])
const prevProducts = [...allProducts];
setProducts(prevProducts);
const updateProductPrice = (newPrice, index) => {
const newProducts = [...products];
newProducts[index].price = newPrice;
setProducts(newProducts);
console.log(allProducts);
}
When I console.log(allProducts), it's showing me an updated array that has the values of newProducts
I think what you are seeing has to do specifically with how JavaScript is storing object references inside your arrays. When you copy an array, you do not do a full re-creation of all the objects in it, even when using the spread operator, you are only copying the references to the objects at each index.
In other words, assuming they share matching indexes, each item at newProducts[i] === allProducts[i] - i.e. is the exact same object instance.
See, for example, https://javascript.info/object-copy and as well there are many references for "shallow" and "deep" copying for objects.
The objects are the same in memory even though I changed the reference.
"A variable assigned to an object stores not the object itself, but its “address in memory” – in other words “a reference” to it."
I used _.cloneDeep from https://lodash.com/docs/4.17.15#cloneDeep
So I changed my code to:
const prevProducts = _.cloneDeep(allProducts);
setProducts(prevProducts);
Another solution: In case you do not want to use lodash.clonedeep package, you can do the same using below:
const array = [{a: 1}]//initial array
const copy = array.map(item => ({...item}))
Related
Has anyone tried getting a particular value inside a prop using a variable?
Normally, getting a particular data in props goes like
const dataValue = props.table.data.rowData.account_number
However in this instance I need the last part of the props to be a variable, because
the account_number value is not fixed and the name varies.
So is there a way that I use a variable instead of adding a fixed name on the past part of the prop call?
Like so:
let theVariable = *somethingNew*;
const dataValue = props.table.data.rowData.theVariable
You can do this through property bracket access:
let theVariable = somethingNew;
const dataValue = props.table.data.rowData[theVariable]
This can also be used during object creation:
const dataValue = {
[theVariable]: {}
}
Yes. You can do like this:
let theVariable = *somethingNew*;
const dataValue = props.table.data.rowData[theVariable];
I have this problem which may sounds stupid but I don't really understand the whys.
I declare it as a variable: let [ randomQuoterState, setrandomQuoterState ] = useState([]); Then pass it into a component inside the return: <UserOutput set={setrandomQuoterState} current={randomQuoterState} number={1}/>
The following code is inside the component:
let toSet = [];
toSet[props.number] = quoteArray[Math.floor(Math.random() * quoteArray.length)];
let quote = props.current;
if (quote[props.number]){
delete quote[props.number];
console.log("deleted")
}else {
console.log("this does not exist");
}
console.log(typeof(toSet[props.number]));
console.log(toSet[props.number].lenght)
console.log(toSet[props.number]);
quote[props.number] = toSet[props.number][Math.floor(Math.random() * toSet[props.number].lenght)];
props.set(quote);
The Consol displays it as an array, but the typeof function says its an object, and it doesn't have a length property.
I would appreciate any help or explanation, I thought about it a lot, but I couldn't come up with anything.
Arrays are objects in Javascript. In fact, there is no array type.
To see if it is an array, you should try console.log((toSet[props.number]).constructor.name) and do your checks against toSet[props.number] instanceof Array.
Do not use (toSet[props.number]).constructor.name == 'Array' in your comparisons, because you could have something that has inherited from Array but whose constructor name is different.
In JavaScript both object and array are of type Object.
In case you want to determine exact type, you can use constructor property.
const data = {};
data.contructor.name === 'Object'; // Returns True
const data = [];
data.contructor.name === 'Object'; // Returns True
data.contructor.name === 'Object'; // Returns False
Above can used to determine String, Date etc as well.
Alternatively you can use libraries like lodash which has function for these things.
However that is overkill I guess.
Hope it helps.
So I'm pulling some data from my DB and it comes out in the form of an object. I'd like to take this object and turn it into separate constants. This is the object
(sorry for link, I dont have enough rep to post images)
https://i.imgur.com/ay7cI29.png
How can I take values out from the object and assign them to constants. I want to get the value of the field 'address' which is the apartment's address and assign it to a const called rent.
You can use destructuring for this very easily:
const { rent: address, ownerEmail, otherProperty1, otherProperty2 } = yourObject;
console.log(rent); //The address
You can use destructuring. So, let's say you only want the id from the object. You could do something like the following:
const { id } = await fetchMyData();
you can access the object properties like,
1.
const {ac, address} = yourObject;
2.
const ac = yourObject.ac;
const address = yourObject.address;
Hope this can help!
I have this code that I am testing on jsfiddle
onVote = (dir, index) => {
console.log(this.state)
const products = [...this.state.products]
products[index].votes = dir ? products[index].votes + 1 : products[index].votes - 1
console.log(this.state.products[index].votes)
// this.setState({products})
};
https://jsfiddle.net/hkL3wug7/2/
However, even though I am not setting State, the console log shows that the state is changes every time I click on the plus and minus signs.
I did the same as in this article https://medium.com/#giltayar/immutably-setting-a-value-in-a-js-array-or-how-an-array-is-also-an-object-55337f4d6702
const newState = [...state] // clone the array
newState[action.index].done = true
return newState
as far as I understand
(it is not duplicate of the other question, I am not asking for an efficient way)
As #Carcigenicate mentioned, you have created a shallow copy of the array which means you have a new array pointing to the same objects in the original.
To avoid mutating the original object, you will need to also create a copy of the one you would like to mutate, e.g.:
// Shallow copy of the array
const products = [...this.state.products];
// Shallow copy of the object within the array
const updatedProduct = { ...products[index] };
// Update the copy of the object
updatedProduct.votes = dir ? updatedProduct.votes + 1 : updatedProduct.votes - 1;
// Replace the object with the updated copy
products[index] = updatedProduct;
As #Carcigenicate mentioned in the comment, using the spread operator creates a shallow copy of the array. This is creating a problem for you because the expanded version of the array contains Objects which are passed by reference. So even though your local variable products is a new copy of this.state.products, they both contain references to the same Objects.
To achieve what you are trying to do, you would have to clone the objects in this.state.products. One possible way to do this is using Object.assign and replace your const products = [...this.state.products] with:
const products = [
Object.assign({}, this.state.products.Orange),
Object.assign({}, this.state.products.Apples),
Object.assign({}, this.state.products.Bananas)
]
So I'm using ngrx for managing the state in my application. I tried to add a new property (selected shifts) which should look like this:
state: {
shifts: {
selectedShifts: [
[employeeId]: [
[shiftId]: shift
]
]
}
}
at the moment, my state looks like this:
state: {
selectedShifts: {
[employeeId]: {
[shiftId]: shift
}
}
}
so as you can see, my "selected shift" is a property, not an array - which makes it diffictult to add/remove/query the state.
How do I compose the state to look like I want it?
This is what I tried in the reducer:
return {
...state,
selectedShifts: {
...state.selectedShifts,
[action.payload.employeeId]: {
...state.selectedShifts[action.payload.employeeId],
[action.payload.shiftId]: action.payload[shift.shiftId]
}
}
};
Now when I try to return the state in the way I'd like to, this is the result:
state: {
selectedShifts: {
[action.payload.employeeId]:
[0]: {[action.payload.shiftId]: { shift }}
}
}
What am I missing here? When I try to replace the {} items which should be [] this error comes up: "," expected.
Oh yea, I would like the index of the array to be the id of the specific shift and not [0], [1]...
Is this possible at all?
Would it be a bad idea to change the index from numerics to the actual shift's id?
Array length kind of miss behaves when you add data at numeric index points. This might get you into problems with array methods using length join, slice, indexOf etc. & array methods altering length push, splice, etc.
var fruits = [];
fruits.push('banana', 'apple', 'peach');
console.log(fruits.length); // 3
When setting a property on a JavaScript array when the property is a valid array index and that index is outside the current bounds of the array, the engine will update the array's length property accordingly:
fruits[5] = 'mango';
console.log(fruits[5]); // 'mango'
console.log(Object.keys(fruits)); // ['0', '1', '2', '5']
console.log(fruits.length); // 6
There is no problem selecting / updating state from object, it's just a bit different from what you're probably used to. With straight hashmap { objectId: Object } finding the required object to update / remove is the fastest possible if changes are defined for object id.
I know your problem is related to NGRX but reading Redux immutable patterns is going to definitely help you out here for add / update / remove objects from the state. https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns
Generally you don't want to have arrays in state ( at least large arrays ) object hashmaps are a lot better.
To get array of your selected user shifts for views you could do something like. Note this is not a shift indexed array just array of shifts under userId property. From original state form following state.
state: {
selectedShifts: {
[employeeId]: {
[shiftId]: shift
}
}
}
const getSelectedShiftsAsArray = this.store.select( getSelectedShifts() )
.map(
userShifts => {
// get array of object ids
const userIds = Object.keys( userShifts );
const ret = {};
for( const userId of userIds ) {
const collectedShifts = [];
// convert Dictionary<Shift> into a Shift[]
// get array of shift ids
const shiftIds = Object.keys( userShifts[userId] );
// map array of shift ids into shift object array
collectedShifts = shiftIds.map( shiftId => userShifts[shiftId] );
// return value for a userId
ret[userId] = collectedShifts;
}
return ret;
});
Code is completely untested and just for a reference one level up from pseudocode. You could easily convert that into a NGRX selector though. The state is there just for the storage, how you model it for use in components is upto selector functions & components themselves.
If you really really need it you could add.
ret[userId].shiftIds = shiftIds;
ret[userId].shifts = collectedShifts;
But it really depends on how you plan to use these.
From my personal experience I would separate shift entities from selectedShifts but how you organise your state is completely up to you.
state: {
shifts: {
// contains shift entities as object property map id: entity
entities: Dictionary<Shift>,
selectedShifts: [
[employeeId]: number[] // contains ids for shifts
]
}
}
Now updating / removing and adding a shift would just be setting updated data into path shifts.entities[entityId]
Also selectedShifts for employeeId would be about checking if id is already in there and appending it into an array if it wasn't. ( If these arrays are humongous I'd go with object hash here too for fast access. <employeeId>: {shiftId:shiftId} ).
Check also:
redux: state as array of objects vs object keyed by id