如何在 React 中编辑多个输入控制组件?

IT技术 javascript reactjs forms input components
2021-04-19 11:10:13

我有一个组件将联系人对象存储为状态 - {firstName: "John", lastName: "Doe", phone: "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 });
  }
6个回答

有一种“简单”的方法可以做到这一点,还有一种“聪明”的方法。如果你问我,以聪明的方式做事并不总是最好的,因为以后可能更难和我一起工作。在这种情况下,两者都是可以理解的。

旁注:我要请您考虑的一件事是,您是否需要更新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转译您的代码,例如使用预设es2015react以及插件stage-0

下面是更新的例子,使用对象解构从状态中获取联系, 传播操作符创建一个更新的联系对象而不是改变现有的对象,通过扩展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是一个柯里化函数:使用 a 调用它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>
    );
  }
}
如果这确实是一个人可以做的事情,那么这听起来像是一个聪明的解决方案。
2021-06-03 11:10:13
永远不需要遍历数组来找到正确的联系人。假设您有一个ContactList组件显示所有联系人并允许用户选择一个进行编辑。将 selectedContact 存储在 状态ContactList然后,只要您有 selectedContact,就渲染<ContactEdit contact={this.state.selectedContact} onChange={...} />如有必要,将更改从 ContactEdit 传播回 ContactList。
2021-06-05 11:10:13
我认为您已经为“智能”方式颠倒了 handleChange 中参数的顺序。不应该是:(propertyName, event) 而不是 (event, propertyName) 吗?
2021-06-14 11:10:13
我没想到将接触从状态中拉出来,改变它的道具,然后再把它放回去。那是天才!我的状态实际上是一组联系人对象 {contacts: [{...}, {...}] 但我假设我也可以拉出一个人?在那一点上,是否值得通过这个数组进行查询以找到一个特定的对象?
2021-06-17 11:10:13
在 handleChange 中使用 e.target.name 怎么样(当然,在输入上设置 'name' 属性)这样你就可以省略第二个参数
2021-06-19 11:10:13

ES6 一种线性方法

<input type="text" 
       value={this.state.username}
       onChange={(e) => this.setState({ username: e.target.value })}
       id="username"/>
关于使用“箭头函数”引入的性能问题的文章:flexport.engineering/...
2021-05-30 11:10:13
2/2 GitHub 存储库:github.com/flexport/reflective-bind(有关作者的 Medium 文章/帖子,请参阅我的其他评论。)
2021-06-01 11:10:13
性能警告:如果您需要响应输入更改,您可能希望消除该功能的抖动,因此上述功能将无法正常工作,因为它会componentDidUpdate在每次按键时触发
2021-06-09 11:10:13

有两种方法可以更新嵌套对象的状态:

  1. 使用 JSON.parse(JSON.stringify(object)) 创建对象的副本,然后更新副本并将其传递给 setState。
  2. 使用react-addons 中不变性助手,这是推荐的方式。

你可以在这个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')
);
恕我直言,在通过将其放回更新状态之前改变状态中存在的对象是好的。JSON.parse(JSON.stringify(...))尽管在项目中使用all 似乎是一种不必要的谨慎方式。
2021-05-22 11:10:13
这是正确的做法。你的其余代码和我发布的一样吗?
2021-05-30 11:10:13
感谢您的帮助,但是您如何需要插件?我使用 var update = require('react-addons-update'); 如您发送的页面所示,然后将 React.adddons.update 更改为仅更新,但它不会让我更改任何输入
2021-06-06 11:10:13
如果没有,this.state将直接修改。根据 React 文档:“永远不要直接改变 this.state,因为之后调用 setState() 可能会替换你所做的改变。把 this.state 当作是不可变的。” ( facebook.github.io/react/docs/component-api.html )
2021-06-09 11:10:13
@Mark 为什么React.addons.update在您看来推荐使用?我认为如果您能在答案中澄清这一点会很有用。另外,为什么要使用JSON.parse(JSON.stringify(this.state.contact))而不仅仅是this.state.contact
2021-06-12 11:10:13

最简洁的方法

这是我在我的简单应用程序中使用的一种方法。这是React 中推荐的方法,它非常简洁。它非常接近 ArneHugo 的回答,我也很感谢。这个想法是它和react形式站点的混合。我们可以使用每个表单输入的 name 属性来获取特定的 propertyName 并基于它更新状态。这是我在 ES6 中用于上述示例的代码:

class ContactEdit extends React.Component {

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

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

区别:

  • 我们不需要将状态分配为值属性。不需要值
  • onChange 方法不需要在函数调用中包含任何参数,因为我们使用 name 属性代替
  • 我们在开始时声明每个输入的名称和值,并使用它们在代码中正确设置状态,我们使用球拍作为名称,因为它是一个属性。

我们这里的代码较少,并且可以通过非常聪明的方式从表单中获取任何类型的输入,因为 name 属性对于每个输入都有一个唯一的值。请参阅我在 CodPen 中为我的早期实验性博客应用程序提供的工作示例

这是通用的;

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

并像这样使用;

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