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?

1

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

Comments

0

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

this.setState(R.assocPath(args, ev.target.value))

Comments

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);

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.