Says my state is like this:
{
item:{
a:'a',
b:'b'
}
}
Then I'm able to pull a from item by doing:
const { a } = this.state.item
but can pull dynamically using {} of es6 ?
For example const { variable } = this.state.item, where variable can be a or b.
As 4castle pointet out, you could use Computed object property names and destructuring with an additional key/value pair variables for destructuring.
var object = { item: { a: 'a0', b: 'b0' } },
key = 'b',
value;
({ [key]: value } = object.item);
console.log(value);
const handleChange = (e) => {
const { name, value } = e.target;
const { [name]: property, ...rest } = coupon;
setNewValue({ [name]: value, ...rest });
}
Related
This question already has answers here:
Accessing nested JavaScript objects and arrays by string path
(44 answers)
Closed last month.
I've redux store which looks like below.
store = {
a: {
b: { value: "" }
},
c: {
d: { value: "" }
}
};
From my component, I want to pass like below to minipulate value of b.
dispatch(doSomething({ value: 'my-value', place: "a.b" });
And, in my reducers, I want to handle like below.
doSomething: (state, action) => {
const { value, place } = action.payload;
state[place].value = value; // here, i want to refere -> state.a.b.value = value;
}
I don't want to handle like this:
doSomething: (state, action) => {
const { value, place } = action.payload;
if (place === 'a.b') state.a.b.value = value;
else if (place === 'c.d') state.c.d.value = value;
}
How can I achieve it?
What you're trying to do is access/modify the object by string path. Maybe you can do something like this.
let store = {
a: {
b: { value: "" }
},
c: {}
};
console.log(store)
function setNestedValue(obj, path, value) {
let i;
path = path.split('.');
for (i=0;i<path.length-1;i++) obj = obj[path[i]];
obj[path[i]] = value
}
setNestedValue(store,"a.b", "xx")
console.log(store)
const {value, place } = payload
let cur=state;
place.split(".").forEach((key, idx)=> {
if(idx < place.length -1) {
cur[key] =value;
} else{
cur=cur[key];
}
});
sent via mobile.. couldn't format much.. you can improvise this..
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});
}
I have a React component that has some form fields in it:
<TextField
label="Description"
id="description"
value={company.companyDescription}
onChange={updateFormField("companyDescription")}
></TextField>
and a function that updates my company state whenever the values change:
const updateFormField = (property: string) => (event: ChangeEvent<HTMLInputElement>) => {
setCompany(prev => ({ ...prev, [property]: event.target.value }))
}
This means that whenever a form field changes I'd like to create a new copy (hence the spread operator) of the old object.
My problem is that company has nested properties in it, like company.location.address:
{
name: "some company",
location: {
address: "some street"
}
// ...
}
Is there a way to refactor the above function to update the nested properties? For example something like:
const updateFormField = (property: string[]) => (event: ChangeEvent<HTMLInputElement>) => {
setCompany(prev => ({ ...prev, [...property]: event.target.value }))
}
I don't think there's a particularly neat solution to this, however it should be possible to loop through selecting/adding the new path:
const updateFormField = (property: string[]) => (event: ChangeEvent<HTMLInputElement>) => {
setCompany(prev => {
// Take a copy of the state
const newObj = { ...prev }
// Set up a refernce to that object
let selected = newObj
// Loop through each property string in the array
for (let i = 0; i < property.length; i++) {
// If we're at the end of the properties, set the value
if (i === property.length - 1) {
selected[property[i]] = event.target.value
} else {
// If the property doesn't exist, or is a value we can't add a new property to, set it to a new object
if (typeof selected[property[i]] !== 'object') {
selected[property[i]] = {}
}
// Update our refernce to the currently selected property and repeat
selected = selected[property[i]]
}
}
// Return the object with each nested property added
return newObj
)}
}
Plain JS working example of the same method:
const test = (prev, property, value) => {
const newObj = { ...prev
}
let selected = newObj
for (let i = 0; i < property.length; i++) {
if (i === property.length - 1) {
selected[property[i]] = value
} else {
if (typeof selected[property[i]] !== 'object') {
selected[property[i]] = {}
}
selected = selected[property[i]]
}
}
return newObj
}
console.log(test(
{"a": "1"},
["b", "c", "d"],
100
))
console.log(test(
{"a": "1"},
["a", "b"],
100
))
console.log(test(
{"a": {"b": {"c": 1}}},
["a", "b", "c"],
100
))
Object.assign() and dynamically finding the inner reference should do it. I'm assuming the input type of string[] above indicates the nested path is an array like ['company', 'location', 'address']:
const updateFormField = (property: string[]) => (event: ChangeEvent<HTMLInputElement>) => {
setCompany(prev => {
const copy = Object.assign({}, prev);
let ref = copy;
for (let i = 0; i < property.length - 1; i++) {
ref = ref[property[i]];
}
ref[property[property.length - 1]] = event.target.value
return copy;
});
}
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;
}
Says my state is like this:
{
item:{
a:'a',
b:'b'
}
}
Then I'm able to pull a from item by doing:
const { a } = this.state.item
but can pull dynamically using {} of es6 ?
For example const { variable } = this.state.item, where variable can be a or b.
As 4castle pointet out, you could use Computed object property names and destructuring with an additional key/value pair variables for destructuring.
var object = { item: { a: 'a0', b: 'b0' } },
key = 'b',
value;
({ [key]: value } = object.item);
console.log(value);
const handleChange = (e) => {
const { name, value } = e.target;
const { [name]: property, ...rest } = coupon;
setNewValue({ [name]: value, ...rest });
}