反应表单错误将类型文本的受控输入更改为不受控制

Posted

技术标签:

【中文标题】反应表单错误将类型文本的受控输入更改为不受控制【英文标题】:React form error changing a controlled input of type text to be uncontrolled 【发布时间】:2017-07-25 04:49:35 【问题描述】:

我正在创建一个使用受控输入做出反应的简单表单。在我的组件状态中,状态上有 2 个属性“clientName”和“license”。改变这些工作正常。但是有一个“运输”属性是一个对象。更改任何运输属性都会出错。例如,如果我更改“address1”,只要在 handleShippingChange 函数中设置状态,我就会收到错误:

警告:TextField 正在将文本类型的受控输入更改为 不受控制。输入元素不应从受控切换到 不受控制(反之亦然)。决定使用受控或 在组件的生命周期内不受控制的输入元素。

我怀疑这与我如何定义这些 TextField 的值以及我如何设置运输属性的状态有关。我究竟做错了什么?我的组件的代码如下:

import React, Component from 'react';
    import TextField from 'material-ui/TextField';
    import RaisedButton from 'material-ui/RaisedButton';
    import 'whatwg-fetch';

    class Clients extends Component 
      constructor() 
        super();
        this.state = 
          "clientName": "client name",
          "shipping": 
            "name": "name",
            "address1": "address 1",
            "address2": "address 2",
            "city": "city",
            "state": "state",
            "zip": "zip",
            "country": "country"
          ,
          "license": "license"
        ;

        this.handleChange = this.handleChange.bind(this);
        this.handleShippingChange = this.handleShippingChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
      ;       

      handleChange(event) 
        this.setState(
          [event.target.name]: this.getFieldValue(event.target)
        );
      ;

      handleShippingChange(event) 
        this.setState(
          shipping: 
            [event.target.name]: this.getFieldValue(event.target)
          
        );
      ;

      getFieldValue(target) 
        return target.type === 'checkbox' ? target.checked : target.value;
      ;

      handleSubmit = (event) => 
        event.preventDefault();

        // do some stuff
      ;   

      render() 
        return <div>
          <h1>
            Clients Page
          </h1>

          <form onSubmit=this.handleSubmit>
            <TextField
              hintText="Enter the client name"
              floatingLabelText="Client Name"
              value=this.state.clientName
              onChange=this.handleChange
              name="clientName"
            />
            <h2>Shipping Info</h2>
            <TextField
              hintText=""
              floatingLabelText="Name"
              value=this.state.shipping.name
              onChange=this.handleShippingChange
              name="name"
            />
            <br />
            <TextField
              hintText=""
              floatingLabelText="Address Line 1"
              value=this.state.shipping.address1
              onChange=this.handleShippingChange
              name="address1"
            />
            <br />
            <TextField
              hintText=""
              floatingLabelText="Address Line 2"
              value=this.state.shipping.address2
              onChange=this.handleShippingChange
              name="address2"
            />
            <br />
            <TextField
              hintText=""
              floatingLabelText="City"
              value=this.state.shipping.city
              onChange=this.handleShippingChange
              name="city"
            />
            <br />
            <TextField
              hintText=""
              floatingLabelText="State"
              value=this.state.shipping.state
              onChange=this.handleShippingChange
              name="state"
            />
            <br />
            <TextField
              hintText=""
              floatingLabelText="Zip Code"
              value=this.state.shipping.zip
              onChange=this.handleShippingChange
              name="zip"
            />
            <br />
            <TextField
              hintText=""
              floatingLabelText="Country"
              value=this.state.shipping.country
              onChange=this.handleShippingChange
              name="country"
            />
            <br />
            <TextField
              hintText=""
              floatingLabelText="License"
              value=this.state.license
              onChange=this.handleChange
              name="license"
            />
            <br />
            <RaisedButton label="OK" primary=true type="submit" />
          </form>
        </div>
      ;
    

    export default Clients;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

【问题讨论】:

【参考方案1】:

受控/非受控输入表示&lt;input&gt;字段是否有值。

// This is a controlled input
<input value="foo"/>

// This is an uncontrolled input
<input value=null/>

这个想法是您不想从受控输入更改为不受控输入。两种类型的输入行为不同,这可能会导致错误和/或不一致。

最简单的解决方法是确保始终有一个默认值(在空字段的情况下,默认值为空字符串'')。

另外,请注意一致类型通常比可空类型更好,因为您可以保证某个值的类型。这有助于减少因空检查导致的开销 (if (val != null) /* ...etc */ )

但是如果你只想要一行修复,你也可以在 jsx 中提供默认值:

<input value=value || ''/>

【讨论】:

谢谢,这是我的意图,将构造函数中的状态初始化为一个虚拟值(或空字符串)。将条件放在花括号内可修复错误消息,但不能解决底层设计问题。现在按照您的建议进行操作后,TextField 如下所示: ... 当我输入一个值并按 Tab 键转到下一个字段并输入一个值时,我的第一个值消失了。有什么建议可以解决这个问题吗? 看起来你找到了你的错误,React 真的没有什么魔力。因此,当 UI 错误时,这意味着您正在传递/创建错误的数据。这总是你需要去检查哪里出了问题。 这个答案应该被批准【参考方案2】:

我刚刚遇到了同样的问题,但事实证明这个错误要简单得多(边界尴尬)

我在定义 onChange 函数时忘记添加 event 参数。

handleChange(event) 
  ...          ^-this
;

奇怪的是我没有收到event is undefined 错误...希望这对某人有所帮助。

【讨论】:

【参考方案3】:

根本问题是我在运输对象上设置属性的方式不会将新属性值与原始属性值合并。因此,警告不是针对我正在编辑的 TextField,而是针对其他正在被吹走的运输 TextField。我不确定这是否是公认的做法,因为很难找到您正在使用该州的对象的示例。但是,将 handleShipping 方法更改为此解决了我的问题:

handleShippingChange(event) 
  var shipping = this.state.shipping;
  shipping[event.target.name] = this.getFieldValue(event.target);

  this.setState(
    shipping: shipping
  );
;

基本上,我是根据状态创建现有运输对象的副本,对其进行更改并将整个运输对象设置为等于更改后的副本。

【讨论】:

顺便说一句,这是对运输对象的变异,并且可以打破一些对不可变状态的反应假设。您需要创建一个新的运输对象var shipping = ...this.state.shipping, [event.target.name]: fieldVal 或者代替var shipping = this.state.shipping改变状态,你可以使用cloneDeep from lodash: lodash.com/docs/4.17.4#cloneDeep

以上是关于反应表单错误将类型文本的受控输入更改为不受控制的主要内容,如果未能解决你的问题,请参考以下文章

电子邮件输入警告 - 组件正在将文本类型的受控输入更改为不受控制

警告:TextField 正在将文本类型的受控输入更改为不受控制

Material UI Select Component - 一个组件正在将文本类型的受控输入更改为不受控制

即使一切都受到控制,也会出现不受控制的输入错误

不受控制的输入 React Hooks

组件正在将不受控制的自动完成更改为受控