Как отредактировать несколько элементов, управляемых данными в React?

У меня есть компонент, который хранит контактный объект как состояние - {firstName: "John", lastName: "Doe", телефон: "1234567890} Я хочу создать форму для редактирования этого объекта, но если я хочу, чтобы входы удерживая значение исходного параметра контакта, мне нужно сделать каждый вход управляемым компонентом. Однако я не знаю, как создать функцию handleChange, которая будет настраиваться для каждого параметра, потому что мое состояние содержит только {contact: {... }} Ниже приведено то, что у меня есть -

  getInitialState: function () {
    return ({contact: {}});
  },
  handleChange: function (event) {
    this.setState({contact: event.target.value });
  },
  render: function () {
    return (
        <div>
          <input type="text" onChange={this.handleChange} value={this.state.contact.firstName}/>
          <input type="text" onChange={this.handleChange} value={this.state.contact.lastName}/>
          <input type="text" onChange={this.handleChange} value={this.state.contact.lastName}/>
        </div>
      );
    }

Я хочу, чтобы в моем handleChange я могу сделать что-то вроде

  handleChange: function (event) {
    this.setState({contact.firstName: event.target.value });
  }

Ответ 1

Там есть "простой" способ сделать это и "умный" способ. Если вы спросите меня, что делать, умный способ не всегда лучший, потому что мне может быть труднее работать с ним позже. В этом случае оба варианта вполне понятны.

Боковое примечание. Одна вещь, о которой я прошу вас подумать, - вам нужно обновить объект contact, или вы можете просто сохранить firstName и т.д. непосредственно в состоянии? Может быть, у вас много данных в состоянии компонента? Если это так, вероятно, неплохо было бы разделить его на более мелкие компоненты с более узкими обязанностями.

"Простой" способ

  changeFirstName: function (event) {
    const contact = this.state.contact;
    contact.firstName = event.target.value;
    this.setState({ contact: contact });
  },
  changeLastName: function (event) {
    const contact = this.state.contact;
    contact.lastName = event.target.value;
    this.setState({ contact: contact });
  },
  changePhone: function (event) {
    const contact = this.state.contact;
    contact.phone = event.target.value;
    this.setState({ contact: contact });
  },
  render: function () {
    return (
      <div>
        <input type="text" onChange={this.changeFirstName.bind(this)} value={this.state.contact.firstName}/>
        <input type="text" onChange={this.changeLastName.bind(this)} value={this.state.contact.lastName}/>
        <input type="text" onChange={this.changePhone.bind(this)} value={this.state.contact.phone}/>
      </div>
    );
  }

"умный" способ

  handleChange: function (propertyName, event) {
    const contact = this.state.contact;
    contact[propertyName] = event.target.value;
    this.setState({ contact: contact });
  },
  render: function () {
    return (
      <div>
        <input type="text" onChange={this.handleChange.bind(this, 'firstName')} value={this.state.contact.firstName}/>
        <input type="text" onChange={this.handleChange.bind(this, 'lastName')} value={this.state.contact.lastName}/>
        <input type="text" onChange={this.handleChange.bind(this, 'phone')} value={this.state.contact.lastName}/>
      </div>
    );
  }



Обновление: те же примеры, что и для ES2015 +

В этом разделе содержатся те же примеры, что показаны выше, но с использованием функций из ES2015 +.

Чтобы поддерживать следующие функции в браузерах, вам необходимо передать ваш код Babel, используя, например, пресеты es2015 и react, и плагин stage-0.

Ниже приведены обновленные примеры, используя разрушение объектов, чтобы получить контакт из состояния, распространить оператор на создайте обновленный контактный объект вместо мутирования существующего, создавая компоненты как Classes расширяя React.Component, и используя функции стрелок до создавать обратные вызовы, поэтому нам не нужно bind(this).

"Простой" способ, ES2015 +

class ContactEdit extends React.Component {

  changeFirstName = (event) => {
    const { contact } = this.state;
    const newContact = {
      ...contact,
      firstName: event.target.value
    };
    this.setState({ contact: newContact });
  }
  changeLastName = (event) => {
    const { contact } = this.state;
    const newContact = {
      ...contact,
      lastName: event.target.value
    };
    this.setState({ contact: newContact });
  }
  changePhone = (event) => {
    const { contact } = this.state;
    const newContact = {
      ...contact,
      phone: event.target.value
    };
    this.setState({ contact: newContact });
  }

  render() {
    return (
      <div>
        <input type="text" onChange={this.changeFirstName} value={this.state.contact.firstName}/>
        <input type="text" onChange={this.changeLastName} value={this.state.contact.lastName}/>
        <input type="text" onChange={this.changePhone} value={this.state.contact.phone}/>
      </div>
    );
  }
}

"умный" способ, ES2015 +

Обратите внимание, что handleChangeFor является функцией curried: Вызов с помощью propertyName создает функцию обратного вызова, которая при вызове обновляет [propertyName] (новый) контактный объект в состоянии.

class ContactEdit extends React.Component {

  handleChangeFor = (propertyName) => (event) => {
    const { contact } = this.state;
    const newContact = {
      ...contact,
      [propertyName]: event.target.value
    };
    this.setState({ contact: newContact });
  }

  render() {
    return (
      <div>
        <input type="text" onChange={this.handleChangeFor('firstName')} value={this.state.contact.firstName}/>
        <input type="text" onChange={this.handleChangeFor('lastName')} value={this.state.contact.lastName}/>
        <input type="text" onChange={this.handleChangeFor('phone')} value={this.state.contact.lastName}/>
      </div>
    );
  }
}

Ответ 2

ES6 подход с одним лайнером

<input type="text" 
       value={this.state.username}
       onChange={(e) => this.setState({ username: e.target.value })}
       id="username"/>

Ответ 3

Существует два способа обновления состояния вложенного объекта:

Вы можете увидеть, как он работает в этом JS Fiddle. Код также находится ниже:

var Component = React.createClass({
    getInitialState: function () {
    return ({contact: {firstName: "first", lastName: "last", phone: "1244125"}});
  },
  handleChange: function (key,event) {
    console.log(key,event.target.value);

    //way 1 
    //var updatedContact = JSON.parse(JSON.stringify(this.state.contact));
    //updatedContact[key] = event.target.value;

    //way 2 (Recommended)
    var updatedContact = React.addons.update(this.state.contact, {
        [key] : {$set: event.target.value}
    });
    this.setState({contact: updatedContact});
  },
  render: function () {
    return (
        <div>
          <input type="text" onChange={this.handleChange.bind(this,"firstName")} value={this.state.contact.firstName}/>
          <input type="text" onChange={this.handleChange.bind(this,"lastName")} value={this.state.contact.lastName}/>
          <input type="text" onChange={this.handleChange.bind(this,"phone")} value={this.state.contact.phone}/>
        </div>
      );
    }
});

ReactDOM.render(
  <Component />,
  document.getElementById('container')
);

Ответ 4

Вот общий,

handleChange = (input) => (event) => {
    this.setState({
        ...this.state,
        [input]: event.target.value
    });
}

И используйте вот так:

<input handleChange ={this.handleChange("phone")} value={this.state.phone}/>