Nag
Nag

Reputation: 956

Handling multiple form inputs

How do I write a single setState method to update the value for multiple input elements when the state object is nested as shown below?

Note: State shouldn't be mutated

When the state is not nested, we could do it like:

this.setState({ [event.target.name]: event.target.value });

But how do we do it when the state object is nested?

class FormContainer extends Component {

  constructor () {

  this.state = {
      formControls: {
          email: {
            value: ''
          },
          user: {
            value: ''
          },
          password: {
            value: ''
          }
      }
  }

}

 changeHandler = event => {

  const name = event.target.name;
  const value = event.target.value;

  this.setState({
     //code here


  });
}


  render() {
     return (
      <form>

          <input type="email" 
                 name="email" 
                 value={this.state.formControls.email.value} 
                 onChange={this.changeHandler} 
          />

          <input type="text" 
                 name="user" 
                 value={this.state.formControls.name.value} 
                 onChange={this.changeHandler} 
          />

          <input type="password" 
                 name="password" 
                 value={this.state.formControls.password.value} 
                 onChange={this.changeHandler} 
          />

       </form>      
     );
   }

}

Upvotes: 2

Views: 73

Answers (3)

HMR
HMR

Reputation: 39270

You mixed up email and name, here is a working example:

class Component extends React.Component {
  state = {
    formControls: {
      email: {
        value: '',
      },
      name: {
        value: '',
      },
      password: {
        value: '',
      },
    },
  };

  changeHandler = event => {
    const name = event.target.name;
    const value = event.target.value;

    this.setState({
      ...this.state,//copy state
      formControls: {//set state.formControls
        ...this.state.formControls,//copy sate.formControls
        [name]: {//set state.formControls[name]
          ...this.state.formControls[name],//copy state.formControls[name]
          value,//set state.formControls[name].value
        },
      },
    });
  };

  render() {
    return (
      <form>
        <input
          type="email"
          name="email"
          value={this.state.formControls.email.value}
          onChange={this.changeHandler}
        />

        <input
          type="text"
          name="name"
          value={this.state.formControls.name.value}
          onChange={this.changeHandler}
        />

        <input
          type="password"
          name="password"
          value={this.state.formControls.password.value}
          onChange={this.changeHandler}
        />
      </form>
    );
  }
}

//render app
ReactDOM.render(
  <Component />,
  document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Setting nested values with spread will get messy very quickly, let's say I want to change month of data.person.dateOfBirth:

const data = {
  person: {
    dateOfBirth: {
      month: 12,
      year: 1999,
      day: 31,
    },
  },
};

I have to copy every nest level when using spread:

const newDataWithDifferentMonth = {
  ...data, //copy data
  person: {
    ...data.person, //copy data.person
    dateOfBirth: {
      ...data.person.dateOfBirth, //copy data.person.dateOfBirth
      month: 11,
    },
  },
};

To help with that you can write a helper that will clean up the code:

const newDataWithDifferentMonth = set(
  data,
  ['person', 'dateOfBirth', 'month'],
  () => 11
);

Upvotes: 2

CodeZombie
CodeZombie

Reputation: 2087

There are multiple ways to change the nested state object.

  1. You can use immutablejs to deepClone.
  2. You can use the legacy Object.assign() which es5 provides.
  3. spead operator help us to shallow copy the object but deep clone doesnot work.
  4. JSON.parse(JSON.stringify(this.state)) and mutate the necessary nested object.

code:

changeHandler = event => {
          const inputName = event.target.name;
          const inputValue = event.target.value;
             let updatedFormState = Object.assign({}, this.state);
             updatedFormState.form[inputName].value = inputValue;
             this.setState(updatedFormState);
        }

Upvotes: 0

Joe Lloyd
Joe Lloyd

Reputation: 22363

Use prev state and spread

this.setState((prevState) => ({
  ...prevState,
  [name]: value,
}));

Upvotes: 0

Related Questions