反应-更改不受控制的输入


359

我有一个简单的react组件,其形式相信可以控制一个输入:

import React from 'react';

export default class MyForm extends React.Component {
    constructor(props) {
        super(props);
        this.state = {}
    }

    render() {
        return (
            <form className="add-support-staff-form">
                <input name="name" type="text" value={this.state.name} onChange={this.onFieldChange('name').bind(this)}/>
            </form>
        )
    }

    onFieldChange(fieldName) {
        return function (event) {
            this.setState({[fieldName]: event.target.value});
        }
    }
}

export default MyForm;

运行我的应用程序时,出现以下警告:

警告:MyForm正在更改要控制的文本类型的不受控制的输入。输入元素不应从不受控制切换为受控制(反之亦然)。确定在组件的使用寿命中使用受控或不受控制的输入元素

我相信我的输入是有价值的,因此受到控制。我想知道我在做什么错?

我正在使用React 15.1.0

Answers:


509

我相信我的输入是有价值的,因此受到控制。

为了控制输入,其值必须与状态变量的值相对应。

在您的示例中,最初并未满足该条件,因为this.state.name它最初并未设置。因此,输入最初是不受控制的。一旦onChange处理程序被触发的第一次,this.state.name被设定。在这一点上,满足以上条件,并且认为输入受到控制。从不受控制到受控制的过渡会产生上述错误。

通过this.state.name在构造函数中初始化:

例如

this.state = { name: '' };

从一开始就将控制输入,解决问题。有关更多示例,请参见React Controlled Components

与该错误无关,您应该只有一个默认导出。您上面的代码有两个。


7
很难多次阅读答案并遵循想法,但是此答案是讲述故事并让观众同时理解的理想方式。回答级上帝!
surajnew55

2
如果循环中有动态字段怎么办?例如,您将字段名称设置为name={'question_groups.${questionItem.id}'}
user3574492'4

2
动态字段如何工作。我是否在动态生成表单,然后将状态名称设置为内部状态的对象,我是否还必须首先手动将状态名称手动设置为状态,然后将其传输至我的对象?
约瑟夫

13
这个答案有点不正确。如果valueprop具有非null / undefined值,则控制输入该prop不需要与状态变量相对应(它可以只是一个常量,并且该组件仍将被视为受控制的)。亚当的答案更正确,应该接受。
ecraig12345 '18

2
是的-从技术上来说这是错误的答案(但有帮助)。关于这一点的注意事项-如果您正在处理单选按钮(其“值”的控制方式有所不同),则即使您的组件已被控制(当前),该反应警告实际上也会引发。 github.com/facebook/react/issues/6779,可以通过添加!!来解决 到isChecked的真实性
割草机

122

当你第一次渲染您的组件,this.state.name没有设置,所以它的计算结果undefined或者null,你最终传递value={undefined}value={null}你的input

当ReactDOM检查某个字段是否受到控制时,它会检查是否value != null(请注意,它!=不是!==),并且由于undefined == null在JavaScript中,它确定该字段不受控制。

因此,在onFieldChange()被调用时,this.state.name将其设置为字符串值,您的输入将从不受控制变为受控制。

如果您this.state = {name: ''}在构造函数中这样做,因为'' != null,您的输入将始终具有一个值,并且该消息将消失。


5
就其价值而言,可能会发生同样的事情this.props.<whatever>,这就是我的问题。谢谢,亚当!

是的 如果传递您定义的计算变量render()或标记本身的表达式(所有计算结果为),也会发生这种情况undefined。很高兴我能帮上忙!
Leigh Brenecki

1
感谢您提供此答案:即使不是公认的答案,它对问题的解释也远胜于“指定一个name”。
machineghost

1
我已经更新了答案,以解释受控输入的工作方式。值得注意的是,这里重要的不是undefined最初传递的是。相反,事实是this.state.name不存在状态变量,这使输入不受控制。例如,具有this.state = { name: undefined };将导致输入受到控制。应该理解,重要的是价值来自何处,而不是价值是什么。
fvgs

1
@fvgs this.state = { name: undefined }仍然会导致不受控制的输入。<input value={this.state.name} />讨价还价React.createElement('input', {value: this.state.name})。因为返回访问对象的不存在的属性undefined,所以这将得出完全相同的函数调用React.createElement('input', {value: undefined})— — name是未设置还是未显式设置为undefined,因此React的行为方式相同。您可以在此JSFiddle中看到此行为。
Leigh Brenecki

52

另一种方法是在输入中设置默认值,如下所示:

 <input name="name" type="text" value={this.state.name || ''} onChange={this.onFieldChange('name').bind(this)}/>

1
<input name =“ name” type =“ text” defaultValue =“” onChange = {this.onFieldChange('name')。bind(this)} />我认为这也可行
gandalf

非常感谢,这正是我想要的。使用该模式时,我应该牢记有任何不利之处或潜在问题吗?
Marcel Otten

这对我很有用,因为字段是通过API动态呈现的,所以安装组件时我不知道字段的名称。这工作了请客!
Jamie-Fenrir Digital Ltd

18

我知道其他人已经回答了这个问题。但是,这里有一个非常重要的因素可能会帮助其他人遇到类似的问题:

您必须onChange在输入字段(例如textField,复选框,单选框等)中添加处理程序。始终通过onChange处理程序处理活动。

例:

<input ... onChange={ this.myChangeHandler} ... />

使用复选框时,可能需要使用来处理其checked状态!!

例:

<input type="checkbox" checked={!!this.state.someValue} onChange={.....} >

参考:https : //github.com/facebook/react/issues/6779#issuecomment-326314716


1
这对我有用,谢谢,是的,我100%同意这一点,初始状态为{},所以检查值将不确定并且使其不受控制,
Ping Woo

13

解决此问题的简单方法是默认设置一个空值:

<input name='myInput' value={this.state.myInput || ''} onChange={this.handleChange} />

8

在构造函数中将字段值设置为“”(空字符串)的一个潜在弊端是,如果该字段是可选字段并且未编辑。除非您在发布表单之前进行一些按摩,否则该字段将作为空字符串而不是NULL保留在数据存储中。

此替代方法将避免使用空字符串:

constructor(props) {
    super(props);
    this.state = {
        name: null
    }
}

... 

<input name="name" type="text" value={this.state.name || ''}/>

6

onChange={this.onFieldChange('name').bind(this)}在输入中使用时,必须将状态空字符串声明为属性字段的值。

错误方式:

this.state ={
       fields: {},
       errors: {},
       disabled : false
    }

正确方法:

this.state ={
       fields: {
         name:'',
         email: '',
         message: ''
       },
       errors: {},
       disabled : false
    }

6

就我而言,我确实缺少了一些琐碎的东西。

<input value={state.myObject.inputValue} />

收到警告时,我的状态如下:

state = {
   myObject: undefined
}

通过更改状态以引用我的值的输入,我的问题得以解决:

state = {
   myObject: {
      inputValue: ''
   }
}

2
感谢您帮助我理解我所遇到的真正问题
rotimi-best

3

如果组件上的props作为状态传递,请为输入标签设置默认值

<input type="text" placeholder={object.property} value={object.property ? object.property : ""}>


3

为此的更新。对于React Hooks使用const [name, setName] = useState(" ")


感谢4更新约旦,此错误有点难以解决
Juan Salvador

为什么" """呢?这将导致您丢失任何提示文本,并且如果单击并键入,则在输入任何数据之前都会有一个空白。
约书亚·韦德

1

通常,仅当在应用程序启动时您不控制文件的值,并且在某些事件或某些函数被触发或状态改变后,现在尝试控制输入字段中的值时,才会发生这种情况。

首先是无法控制输入的控制权,然后才是控制权。

避免这种情况的最好方法是在组件的构造函数中为输入声明一些值。使输入元素从应用程序开始就具有价值。


0

为了动态设置表单输入的状态属性并使它们保持受控状态,您可以执行以下操作:

const inputs = [
    { name: 'email', type: 'email', placeholder: "Enter your email"},
    { name: 'password', type: 'password', placeholder: "Enter your password"},
    { name: 'passwordConfirm', type: 'password', placeholder: "Confirm your password"},
]

class Form extends Component {
  constructor(props){
    super(props)
    this.state = {} // Notice no explicit state is set in the constructor
  }

  handleChange = (e) => {
    const { name, value } = e.target;

    this.setState({
      [name]: value
    }
  }

  handleSubmit = (e) => {
    // do something
  }

  render() {
     <form onSubmit={(e) => handleSubmit(e)}>
       { inputs.length ?
         inputs.map(input => {
           const { name, placeholder, type } = input;
           const value = this.state[name] || ''; // Does it exist? If so use it, if not use an empty string

           return <input key={name}  type={type} name={name} placeholder={placeholder} value={value} onChange={this.handleChange}/>
       }) :
         null
       }
       <button type="submit" onClick={(e) => e.preventDefault }>Submit</button>
     </form>    
  }
}

0

如果this.state.name为null,则只需创建一个后备广告到''。

<input name="name" type="text" value={this.state.name || ''} onChange={this.onFieldChange('name').bind(this)}/>

这也适用于useState变量。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.