Reputation: 325
I have an interesting question regarding React state and onChange events in the form. In my React component, I am storing information in the state, that includes objects. When a user types in a change, I would like part of that object in the state to change. As an example, here is the state I am talking about:
this.state = {
total: 0,
email: '',
creditCards: [],
selectedCreditCard: {},
billingAddresses: [],
shippingAddresses: [],
selectedBillingAddress: {},
selectedShippingAddress: {},
}
This is the state of the component, and this is my handle change function:
handleChange(event) {
this.setState({
[event.target.name]: event.target.value,
})
}
As you can probably tell, this type of function will not work for objects within the state. As an example, when a user types in their credit card number, I would like this.state.selectedCreditCard.creditCardNumber to be the thing that changes. Of course, with the way it is setup in my form:
<input
type="text"
name="selectedCreditCard.creditCardNumber"
value={selectedCreditCard.creditCardNumber}
onChange={(evt) => handleChange(evt)}
/>
The "name" being passed into the onChange is only a string, and not a pointer to a key value in one of the objects in the state. Therefore, there is no easy way for this function to work except for deconstructing each object in the state and placing every key:value directly in the state instead of within the object; doing this is a possibility, but the code becomes very cumbersome this way. I hope all of this makes sense. Is there any way to manipulate the objects within the state, and have the form pass a pointer to a key in the object, as opposed to a string? Thanks.
Upvotes: 3
Views: 2063
Reputation: 296
I understand from your description that you wish to update a nested child inside a state object attribute like below
//default state
this.state = {
total: 0,
email: '',
creditCards: [],
selectedCreditCard: {},
billingAddresses: [],
shippingAddresses: [],
selectedBillingAddress: {},
selectedShippingAddress: {},
}
to be updated to something like this
//state after handleEvent
{
total: 0,
email: '',
creditCards: [],
selectedCreditCard:{
creditCardNumber:"102131313"
},
billingAddresses: [],
shippingAddresses: [],
selectedBillingAddress: {},
selectedShippingAddress: {},
}
If thats the case. Here's a recursion based solution to create a nested object using a "." based name string.
handleChange(event) {
//use split function to create hierarchy. example:
//selectedCreditCard.creditCardNumber -> [selectedCreditCard , creditCardNumber]
const obj = this.generate(
event.target.name.split("."),
event.target.value,
0
);
this.setState({ ...obj });
}
//use recursion to generate nested object. example
//([selectedCreditCard , creditCardNumber] , 12 , 0) -> {"selectedCreditCard":{"creditCardNumber":"12"}}
generate(arr, value, index) {
return {
[arr[index]]:
index === arr.length - 1 ? value : this.generate(arr, value, index + 1)
};
}
I have created a codesandbox demo for you to explore more here. demo-link
Upvotes: 2