How to update Object in Array dynamically in React state - javascript

I'm fairly new to ReactJS and wrote this function I want to use to update an object in my state. It seems unable to use the "name" param to update my object and I don't really get why. I tried to code it in template literals as well.
const handleAccountingChange = (newValue, name, id) => {
const newState = selected.map((obj) => {
if (obj.id === id) {
return { ...obj, name: newValue };
}
return obj;
});
setSelected(newState);
};
I get no error in the browser console, but it doesn't update my state either. Any idea would be appreciated. I spent 2 hours on google but didn't find anything.

When you call obj.property = 'aaa' you set property to aaa.
What you try to do is update the property contained by the variable name, what you code does is update the property name.
To update a property from a variable you need to use :
const property = 'name'
obj[property] = 'aaa'
equivalente to :
obj.name == 'aaa'
This code solves your probleme :
const handleAccountingChange = (newValue, name, id) => {
// For the exemple I declare selected here
const selected = [ {id: 1, test: 'aaa'}, {id: 2, test: 'bbb'} ];
const newState = selected.map((obj) => {
if (obj.id === id) {
let myObj = {...obj};
myObj[name] = newValue;
return myObj;
}
return obj;
});
return newState; // instead ou can use setSelected(newState)
};
console.log(handleAccountingChange('ccc', 'test', 1));

const handleAccountingChange = (newValue, name, id) => {
const newState = selected.map((obj) => {
if (obj.id === id) {
return { obj[name]= newValue};
}
return obj;
});
setSelected({...newState});
}

Related

Adding dynamic properties to object only if the name is defined

i have a function like this:
const getKeysAs = (key1, key2) => {
return {
[key1]: state.key1,
[key2]: state.key2
}
}
So if state.key1 is 'a' and state.key2 is 'b', calling getKyesAs('one', 'two') would return
{
one: 'a',
two: 'b'
}
Now, if one of the argument is undefined, is there a way to not include it in the returned object ?
You can Conditionally add properties to an Object with object destructuring
const obj = {
...(key1 && { [key1]: state[key1] }),
...(key2 && { [key2]: state[key2] })
};
If some of the args function is undefined, null or 0 (falsy values) then it will no be added to the object.
There is a very scalable way to do it:
const state= {
a: "hello",
}
function getKeysAs (keys) {
return [...arguments].reduce((acc, cur)=> {
const newValue = state[cur] && {[cur]: state[cur]}
return {...acc, ...newValue}
}, {})
}
console.log(getKeysAs("a", "b"))
This way, you can pass as much keys as you need without worrying about scalability & undefined values.
Use Object.assign().
const getKeysAs = (key1, key2) => {
return Object.assign({}, key1 && {[key1]: state[key1]}, key2 && {[key2]: state[key2]});
}
Assuming you actually mean to do state[key1], not state.key1, here is a solution that doesn't create superfluous objects:
const getKeysAs = (...keys) => {
const result = {};
for (const key of keys) {
if (key != null) {
result[key] = state[key];
}
}
return result;
}

React comes back with an unexpected token error when I do a dynamic destructuring of my object

I'm updating an input field with an onChange event in React.
const handleUpdateText = (id, name, text) => {
const newItems = items.map( item => {
if (item.id === id) {
return {...item, [menuLang][name]:text } // <-- error here at ][
}
return item;
} )
setItems(newItems);
}
name and text and name attribute and value of target input field.
menuLang is a state variable (string, eg "en").
The items are something like this:
{
id: 1,
type: "menuitem",
isVisible: true,
en: {
name: "Salad",
desc: "Fresh, seasonal ingredients",
},
price: "10",
},
Without dynamic destructuring it work fine:
const newItem = {...item}
newItem[menuLang][name] = text;
return newItem;
// instead of: return {...item, [menuLang][name]:text }
Any ideas what's the mistake?
use computed property name
let item={id:1,type:"menuitem",isVisible:!0,en:{name:"Salad",desc:"Fresh, seasonal ingredients"},price:"10"};
console.log(item)
let name = 'name'
let text = "Orange"
let menuLang = 'en'
item = {...item,[menuLang]:{...item[menuLang],[name]:text}}
console.log(item)
I don't think there is a mistake here, but I also didn't think what you want to do is possible via destructuring - I think you have to go down the route of your second example here.
const items = [{id:12,name: 'dj' }];
const handleUpdateText = (id = 12, name='ann', text='sample') => {
const newItems = items.map((item) => {
const obj = {...item};
if (item.id === id) {
return {...obj, [menuLang]: {[name]:[text]}};
}
return obj;
});
console.log('item', newItems);
};```

Edit function not saving changes to state data in React

I am trying to provide functionality in my webpage for editing state data.
Here is the state structure
state = {
eventList:[
{
name: "Coachella"
list: [
{
id: 1,
name: "Eminem"
type: "rap"
}
{
id: 2,
name: "Kendrick Lamar"
type: "rap"
}
]
}
]
}
I want to be able to edit the list arrays specifically the id, name, and type properties but my function doesn't seem to edit them? I currently pass data I want to override id name and type with in variable eventData and an id value specifying which row is selected in the table which outputs the state data.
Here is the function code:
editPickEvent = (eventData, id) => {
const eventListNew = this.state.eventList;
eventListNew.map((event) => {
event.list.map((single) => {
if (single.id == id) {
single = eventData;
}
});
});
this.setState({
eventList: eventListNew,
});
};
When I run the code the function doesn't alter the single map variable and I can't seem to pinpoint the reason why. Any help would be great
edit:
Implementing Captain Mhmdrz_A's solution
editPickEvent = (eventData, id) => {
const eventListNew = this.state.eventList.map((event) => {
event.list.map((single) => {
if (single.id == id) {
single = eventData;
}
});
});
this.setState({
eventList: eventListNew,
});
};
I get a new error saying Cannot read property list of undefined in another file that uses the map function to render the state data to the table?
This is the part of the other file causing the error:
render() {
const EventsList = this.props.eventList.map((event) => {
return event.list.map((single) => {
return (
map() return a new array every time, but you are not assigning it to anything;
editPickEvent = (eventData, id) => {
const eventListNew = this.state.eventList.map((event) => {
event.list.forEach((single) => {
if (single.id == id) {
single = eventData;
}
});
return event
});
this.setState({
eventList: eventListNew,
});
};
const editPickEvent = (eventData, id) => {
const updatedEventList = this.state.eventList.map(event => {
const updatedList = event.list.map(single => {
if (single.id === id) {
return eventData;
}
return single;
});
return {...event, list: updatedList};
});
this.setState({
eventList: updatedEventList,
});
}
Example Link: https://codesandbox.io/s/crazy-lake-2q6ez
Note: You may need to add more checks in between for handling cases when values could be null or undefined.
Also, it would be good if you can add something similar to the original data source or an example link.
Turns out primitive values are pass by value in javascript, which I didn't know and why the assignment wasn't working in some of the previous suggested answers. Here is the code that got it working for me:
editEvent = (EventData, id) => {
const eventListNew = this.state.eventList.map((event) => {
const newList = event.list.map((single) => {
return single.id == id ? EventData : single;
});
return { ...event, list: newList };
});
this.setState({
eventList: eventListNew,
});
};

Update fields in nested objects in Typescript / Javascript

In Firestore you can update fields in nested objects by a dot notation (https://firebase.google.com/docs/firestore/manage-data/add-data?authuser=0#update_fields_in_nested_objects). I wonder how to make that work in Typescript / Javascript.
For example the following object:
const user = {
id: 1
details: {
name: 'Max',
street: 'Examplestreet 38',
email: {
address: 'max#example.com',
verified: true
}
},
token: {
custom: 'safghhattgaggsa',
public: 'fsavvsadgga'
}
}
How can I update this object with the following changes:
details.email.verified = false;
token.custom = 'kka';
I already found that Lodash has a set function:
_.set(user, 'details.email.verified', false);
Disadvantage: I have to do this for every change. Is their already a method to update the object with an object (like firestore did)?
const newUser = ANYFUNCTION(user, {
'details.email.verified': false,
'token.custom' = 'kka'
});
// OUTPUT for newUser would be
{
id: 1
details: {
name: 'Max',
street: 'Examplestreet 38',
email: {
address: 'max#example.com',
verified: false
}
},
token: {
custom: 'kka',
public: 'fsavvsadgga'
}
}
Does anyone know an good solution for this? I already found more solutions if I only want to change one field (Dynamically set property of nested object), but no solution for more than one field with one method
I think you are stuck with using a function but you could write it yourself. No need for a lib:
function set(obj, path, value) {
let parts = path.split(".");
let last = parts.pop();
let lastObj = parts.reduce((acc, cur) => acc[cur], obj);
lastObj[last] = value;
}
set(user, 'details.email.verified', false);
if what you want to do is merge 2 objects then it is a bit trickier:
function forEach(target, fn) {
const keys = Object.keys(target);
let i = -1;
while (++i < keys.length) {
fn(target[keys[i]], keys[i]);
}
}
function setValues(obj, src) {
forEach(src, (value, key) => {
if (value !== null && typeof (value) === "object") {
setValues(obj[key], value);
} else {
obj[key] = value;
}
});
}
let obj1 = {foo: {bar: 1, boo: {zot: null}}};
let obj2 = {foo: {baz: 3, boo: {zot: 5}}};
setValues(obj1, obj2);
console.log(JSON.stringify(obj1));
One solution in combination with lodash _.set method could be:
function setObject(obj, paths) {
for (const p of Object.keys(paths)) {
obj = _.set(obj, p, paths[p]);
}
return obj;
}

Javascript proxy a collection to single object

I've never used Proxy before, but I think it should be possible to "merge" an collection of objects into a single object.
It needs to remain "live" because the original fields will have value changes performed on them.
Ignore key collisions at this stage:
Given:
const fields = [{
name: 'hello',
value: 1
},{
name: 'goodbye',
value : 2
}];
Output:
const proxy = { hello:1 , goodbye :2 }
I definitely need to be able to iterate over the proxied object with a for in.
Have start a pen here, but haven't got very far: https://codepen.io/anon/pen/mMRaKw?editors=1111
Is it possible?
Here is a solution with the Proxy target as an empty object (if the Array is proxied, the for in will iterate numbered entries).
const fields = [{name: 'hello',value: 1}, { name: 'goodbye', value: 2}];
let handler = {
get: function(target, name) {
var f = fields.find(f => f.name === name);
return f && f.value;
},
ownKeys: function(target) {
return fields.map(f => f.name);
},
getOwnPropertyDescriptor: function(target, prop) {
return { configurable: true, enumerable: true };
}
};
let prox = new Proxy({}, handler);
// update original
fields[0].value=10;
// change reflected in proxy
console.log('proxy.hello',prox.hello);
for( let i in prox ){
console.log(i)
console.log(prox[i])
}
console.log(prox)
I think you are looking for something like:
const fields = [{name: 'hello',value: 1}, { name: 'goodbye', value: 2}];
let handler = {
get: function(target, name) {
if (name === 'flatten') {
return target.reduce((a, c) => {
a[c.name] = c.value;
return a
}, {});
} else {
return target[name];
}
},
set: function(target, prop, value) {
let obj = target.find(o => o.name === prop);
if(obj) {
obj.value = value;
return true;
}
return false;
}
};
let prox = new Proxy(fields, handler);
console.log('flat obj', JSON.stringify(prox.flatten))
// update original
fields[0].value=10;
// change reflected in proxy
console.log('flatten.hello',prox.flatten.hello);
// update proxy
prox.goodbye = 200;
// change reflected in original
console.log('original', fields[1].value)

Categories

Resources