Nicolas Albarracin
Nicolas Albarracin

Reputation: 217

Update Nested state in react

Hello guys I'm trying to update the state of a nested object in react, I'm currently doing this:

handleChange({target: {id, value}}, type) {
    this.setState(
        state => ({
            dwelling: (Object.assign(state.dwelling, {[id]: {[type]: value}}))
        })
    );
}

it comes from a formgroup:

<FormGroup controlId="spaces">
    <ControlLabel>Dormitorios</ControlLabel>
    <FormControl
        componentClass="select"
        value={dwelling.spaces.dorms}
        placeholder="Seleccione"
        onChange={e => this.handleChange(e, 'dorms')}
    >

The problem is when I update the state of the sub object dwelling.spaces.dorms is created but when I try to place another property it replaces the old one instead of getting added:

Before Dwelling:

{
   address: "",
   currency: "",
   price: 0,
   publicationType: "",
   spaces: {
     closets: "",
     dorms: "",
     pools: ""
   },
   subtype: "",
   type: ""
}

After onChange for dwelling.spaces.dorms

{
   address: "",
   currency: "",
   price: 0,
   publicationType: "",
   spaces: {
     dorms: "3",
   },
   subtype: "",
   type: ""
}

After onChange for dwelling.spaces.closets

{
   address: "",
   currency: "",
   price: 0,
   publicationType: "",
   spaces: {
     closets: "3",
   },
   subtype: "",
   type: ""
}

Upvotes: 1

Views: 4217

Answers (2)

Brian Chojnowski
Brian Chojnowski

Reputation: 21

I keep my form state in a complex object and to simplify my code I use this helper function in my "handleChange" function referenced by TextField, Select, etc..

export function updateObject(obj, keys, value) {
  let key = keys.shift();
  if (keys.length > 0) {
    let tmp = updateObject(obj[key], keys, value);
    return {...obj, [key]: tmp};
  } else {
    return {...obj, [key]: value};
  }
}

React-redux/Material-UI example

let [formState, changeFormState] = useState({});

function handleChange(event) {
  changeFormState(updateObject(formState, event.target.name.split('.'), 
event.target.value));
}

<TextField className={classes.textfield} name='foo.bar' value= 
formstate.foo.bar || ''} onChange={handleChange} />

Upvotes: 2

Kenneth Truong
Kenneth Truong

Reputation: 4176

This example uses ES6 spread operator to keep your old properties which is the equivalent of Object.assign.

So what was happening is you're not keeping your nested value.

this.setState({
  dwelling: {
    ...this.state.dwelling,
    [id]: { 
      ...this.state.dwelling[id],
      [type]: value
    }  
  }
});

In your example you overwrote your value with a new object. Notice the bolded text below.

dwelling: (Object.assign(state.dwelling, {[id]: {[type]: value}}))

In the bolded text it specifically set a new object into state.dwelling without keeping the old values. So what we did is that we used the ES6 spread operator to help merge your old values with the new value

{ 
  ...this.state.dwelling[id],
  [type]: value
} 

Upvotes: 4

Related Questions