Hussam Khatib
Hussam Khatib

Reputation: 660

what is the best way to setState which contains nested Object

I have a form with several fields.
To handle the state I am using controlled components.
The approach I used is shown below.
This used to work Initially , but as the requirement changed I had to use nested Object in defining the initial state.
The handleChange no more works properly .
How can I achieve the same result for nested Object too.

  const [details, setDetails] = useState({
    description: null,
    platform: "Xbox",
    region: "ASIA",
    time:{
      start: "2017-05-24T10:30",
      end: "2017-05-28T10:30",
    },
    contact: {
      email: "",
      phone: "",
      twitter: "",
      discord: "",
    }
  });

  function handleChange(evt) {
    const value = evt.target.value;
    setDetails({
      ...details,
      [evt.target.name]: value,
    });
  }

Sample components

      <TextField
        label="Description"
        onChange={handleChange}
        name="description"
        value={details.description}    
      />
         <TextField
        label="email"
        onChange={handleChange}
        name="email"
        value={details.contact.email}    
      />

Upvotes: 0

Views: 306

Answers (4)

Hozeis
Hozeis

Reputation: 1742

I think this would work the best.

function handleNestedChange(evt) {
  const value = evt.target.value;
  const keys = evt.target.name.split("."); 
  if(keys > 1){
    setDetails({
      ...details,
      [keys[0]]: {
        ...details[keys[0]],
        [keys[1]]: event.target.value
      }
    });
  }else{
     setDetails({
      ...details,
      [keys[0]]: event.target.value
      }
    });
  }
}
<TextField
        label="Email"
        onChange={handleChange}
        name="contact.email"
        value={details.description}    
      />

also if you ever start storing huge nested objects into state take a look at the package immutability-helper. Its makes it easier to update only specific fields in a object and does it much faster then recreating the whole thing.

Upvotes: 2

Sanoodia
Sanoodia

Reputation: 925

You can update nested object like this

<TextField
        label="email"
        onChange={handleChange}
        name="contact_email"
        value={details.contact.email}    
      />

 function handleChange(evt) {
    const value = evt.target.value;
    const name = evt.target.name.split("_"); // [0: contact, 1: email]
     setDetails({ ...details, amount : { ...details[name[0]],  
    [name[1]]: value }})
  }

Upvotes: 0

Nicolas Menettrier
Nicolas Menettrier

Reputation: 1679

Lazy way but I will do something like that:

function handleNestedChange(key, event) {
  setDetails({
    ...details,
    [key]: {
      ...details[key],
      [event.target.name]: event.target.value
    }
  });
}
       <TextField
        label="email"
        onChange={(e) => handleNestedChange("contact", e)}
        name="email"
        value={details.contact.email}    
      />

Upvotes: 0

Viet
Viet

Reputation: 12807

Need orther handleChange for fileds in contact:

  function handleChangeContact(evt) {
    const value = evt.target.value;
    setDetails({
      ...details,
      contact: {
        ...details.contact,      
        [evt.target.name]: value,
      }
    });
  }

 <TextField
    label="email"
    onChange={handleChangeContact}
    name="email"
    value={details.contact.email}    
  />

Upvotes: 0

Related Questions