0

I have some generic code that attempts to update specific states. It's possible to access an object by the keys in an array, e.g:

let x = {person: { name: "Dennis"}} 
console.log(x["person"]["name"])

In react, it is possible (and often used in input validation), to access a specific state-key by array, e.g:

//event.target.name = input field property name="firstName"
this.setState({
    [event.target.name]: event.target.value
});

Which would update this.state.firstName to the inputs value.

I am trying to bind nested complex objects to inputs, to avoid having translation functions. So if my state contains { person: {name : "" } } I want to access it dynamically by this.state["person"]["name"] - which works. I want to use the same notation in setState, because then I can bind my nested state-data to inputs like this: <input name="person.name" /> and in my change handler I can look for periods: if(ev.target.name.split("."))...

However, I can't seem to access the state in the same way in setState, because it's an object, so:

const args = ev.target.name.split(".");
this.setState({
    [args[0]][args[1]]: ev.target.value
});

Is there anyway to do this?

Dennis
  • 909
  • 4
  • 13
  • 30
  • You can do it like in the following question https://stackoverflow.com/questions/43040721/how-to-update-a-nested-state-in-react/43041334#43041334 – Shubham Khatri Apr 30 '18 at 11:42

3 Answers3

1

Turns out this was a bit more complicated than initially thought. By using Object.assign all nested objects kept their immutable properties, which made it impossible to change them. I had to make a hard copy of the state, in order to change it. With the use of _set from lodash.set it could be done in very few lines:

//Create a hard-copy of the state
let stateCopy = JSON.parse(JSON.stringify(this.state));

//Update the value in the state with the input value
_set(stateCopy, ev.target.name, ev.target.value);

//Set the state with the changed value
this.setState(stateCopy);

Edit: Only downside is that currently I copy the entire state in the setState() and not just the delta values.

Dennis
  • 909
  • 4
  • 13
  • 30
0

I like to use ramda for this. It would look like

this.setState(R.assocPath(args, ev.target.value))
trixn
  • 15,761
  • 2
  • 38
  • 55
Tiago Coelho
  • 5,023
  • 10
  • 17
0

Its a little more complicated, you could deep copy the objects:

 const args = ev.target.name.split(".");
 let result = {};
 const root = result;
 let pos = this.state;
 const last = args.pop();
 for(const arg of args) {
   Object.assign(result, pos);
   result = result[arg] || (result[arg] = {});
   pos = pos[arg] || {};
 }
 result[last] = evt.target.value;
 this.setState(root);
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • At first I thought it didn't work because you used pop (which effectively removes the last element from the stack, and so the iteration only loops over the first one), but I have problems with setting read-only properties as well, when result is reassigned after the first iteration. – Dennis Apr 30 '18 at 12:40