【问题标题】:React - changing an uncontrolled inputReact - 改变一个不受控制的输入
【发布时间】:2016-09-22 11:44:59
【问题描述】:

我有一个简单的反应组件,我认为它有一个受控输入的表单:

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

【问题讨论】:

    标签: javascript node.js reactjs react-state


    【解决方案1】:

    我相信我的输入是受控的,因为它有一个值。

    对于要控制的输入,它的值必须与状态变量的值相对应。

    您的示例中最初未满足该条件,因为最初未设置 this.state.name。因此,输入最初是不受控制的。一旦onChange 处理程序第一次被触发,this.state.name 就会被设置。此时,满足上述条件,认为输入受控。这种从不受控到受控的转变会产生上述错误。

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

    例如

    this.state = { name: '' };
    

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

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

    【讨论】:

    • 很难多次阅读答案并遵循这个想法,但这个答案是讲故事和让观众同时理解的完美方式。答案级神!
    • 如果循环中有动态字段怎么办?例如您将字段名称设置为 name={'question_groups.${questionItem.id}'}?
    • 动态字段如何工作。动态生成表单,然后在状态内的对象中设置字段名称,我是否仍然必须先手动设置状态中的字段名称,然后将它们传输到我的对象?
    • 这个答案有点不正确。 如果value 属性具有非空值/未定义值,则控制输入。 属性不需要对应于状态变量(它可以只是一个常量,组件仍然可以被认为是受控的)。亚当的回答更正确,应该被接受。
    • 是的 - 这在技术上是错误的答案(但有帮助)。关于这一点的注释 - 如果您正在处理单选按钮(其“值”的控制方式略有不同),即使您的组件受到控制(当前),这个反应警告实际上也会抛出。 github.com/facebook/react/issues/6779,可以通过添加 !! isChecked 的真实性
    【解决方案2】:

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

    当 ReactDOM 检查某个字段是否受控时,it checks to see if value != null(注意它是 !=,而不是 !==),并且由于 JavaScript 中的 undefined == null,它决定它不受控制。

    所以,当onFieldChange() 被调用时,this.state.name 被设置为一个字符串值,你的输入就会从不受控制变为受控制。

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

    【讨论】:

    • 不管怎样,this.props.&lt;whatever&gt; 也会发生同样的事情,这就是我的问题所在。谢谢,亚当!
    • 是的!如果您传递在 render() 中定义的计算变量或标签本身中的表达式(任何计算结果为 undefined 的内容),也会发生这种情况。很高兴我能帮上忙!
    • 感谢您的回答:即使它不是被接受的答案,它也比仅仅“指定一个name”更好地解释了这个问题。
    • 我已经更新了我的答案以解释受控输入的工作原理。值得注意的是,这里重要的不是最初传递了 undefined 的值。相反,this.state.name 不作为使输入不受控制的状态变量存在的事实。例如,拥有this.state = { name: undefined }; 将导致输入被控制。应该明白,重要的是价值来自哪里,而不是价值是什么。
    • @fvgs 具有this.state = { name: undefined } 仍会导致输入不受控制。 &lt;input value={this.state.name} /&gt; 脱糖到 React.createElement('input', {value: this.state.name})。因为访问对象不存在的属性会返回undefined,所以无论name 没有设置还是显式设置为undefined,这都会计算出完全相同的函数调用——React.createElement('input', {value: undefined}),所以 React 的行为方式相同。 You can see this behaviour in this JSFiddle.
    【解决方案3】:

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

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

    【讨论】:

    【解决方案4】:

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

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

    例子:

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

    当您使用 checkbox 时,您可能需要使用!! 处理其checked 状态。

    例子:

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

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

    【讨论】:

    • 这对我有用,谢谢,是的,我 100% 同意这一点,初始状态是 {},因此检查值将未定义并使其不受控制,
    • 双重爆炸解决方案效果很好,谢谢!
    • 这对我有帮助。谢谢。
    【解决方案5】:

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

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

    【讨论】:

    • 很高兴:)
    【解决方案6】:

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

    这种替代方法将避免空字符串:

    constructor(props) {
        super(props);
        this.state = {
            name: null
        }
    }
    
    ... 
    
    <input name="name" type="text" value={this.state.name || ''}/>
    

    【讨论】:

      【解决方案7】:

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

      方法不正确:

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

      正确方法:

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

      【讨论】:

        【解决方案8】:

        就我而言,我错过了一些非常微不足道的东西。

        &lt;input value={state.myObject.inputValue} /&gt;

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

        state = {
           myObject: undefined
        }
        

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

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

        【讨论】:

        • 感谢您帮助我了解我遇到的真正问题
        【解决方案9】:

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

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

        【讨论】:

          【解决方案10】:

          在初始状态下为 'name' 属性设置一个值。

          this.state={ name:''};

          【讨论】:

            【解决方案11】:

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

            【讨论】:

            • 感谢 4 Jordan 的更新,这个错误有点难以解决
            • 为什么是" " 而不是""?这会导致您丢失任何提示文本,并且如果您单击并键入,您会在输入任何数据之前得到一个偷偷摸摸的空格。
            【解决方案12】:

            如果 this.state.name 为空,只需创建一个回退到 ''。

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

            这也适用于 useState 变量。

            【讨论】:

            • 这对我来说是最好的选择,因为我故意将undefined 作为我所在州的值。投反对票的人可以解释原因吗?页面下方有一个与此相同的响应,但有 55 多个赞成票。
            【解决方案13】:

            对于使用Formik 的人,您需要将特定字段name 的默认值添加到表单的initialValues

            【讨论】:

            • 你最好提供一个例子。很难理解。
            • @DavidPiao 这对我来说很有意义,你能详细说明一下吗?
            【解决方案14】:

            这通常仅在应用程序启动时您没有控制字段的值时发生,并且在某些事件或某些函数触发或状态更改之后,您现在尝试控制输入字段中的值。

            这种对输入没有控制权然后又对其进行控制的转变是导致问题首先发生的原因。

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

            【讨论】:

              【解决方案15】:

              在我的情况下,组件正在重新渲染并抛出 A component is changing an uncontrolled input of type checkbox to be controlled 错误。事实证明,这种行为是由于未将truefalse 保持为复选框选中状态(有时我得到undefined)。这是我的故障组件的样子:

              import * as React from 'react';
              import { WrappedFieldProps } from 'redux-form/lib/Field';
              
              type Option = {
                value: string;
                label: string;
              };
              
              type CheckboxGroupProps = {
                name: string;
                options: Option[];
              } & WrappedFieldProps;
              
              
              const CheckboxGroup: React.FC<CheckboxGroupProps> = (props) => {
              
                const {
                  name,
                  input,
                  options,
                } = props;
              
                const [value, setValue] = React.useState<string>();
                const [checked, setChecked] = React.useState<{ [name: string]: boolean }>(
                  () => options.reduce((accu, option) => {
                    accu[option.value] = false;
                    return accu;
                  }, {}),
                );
              
                React.useEffect(() => {
                  input.onChange(value);
              
                  if (value) {
                    setChecked({
                      [value]: true, // that setChecked argument is wrong, causes error
                    });
                  } else {
                    setChecked(() => options.reduce((accu, option) => {
                      accu[option.value] = false;
                      return accu;
                    }, {}));
                  }
              
                }, [value]);
              
                return (
                  <>
                    {options.map(({ value, label }, index) => {
                      return (
                        <LabeledContainer
                          key={`${value}${index}`}
                        >
                          <Checkbox
                            name={`${name}[${index}]`}
                            checked={checked[value]}
                            value={value}
                            onChange={(event) => {
                              if (event.target.checked) {
                                setValue(value);
                              } else {
                                setValue(undefined);
                              }
                              return true;
                            }}
                          />
                          {label}
                        </LabeledContainer>
                      );
                    })}
                  </>
                );
              };
              

              为了解决这个问题,我将useEffect 改成了这个

              React.useEffect(() => {
                input.onChange(value);
              
                setChecked(() => options.reduce((accu, option) => {
                    accu[option.value] = option.value === value;
                    return accu;
                  }, {}));
              
              }, [value]);
              
              

              这使得所有复选框都保持其状态为truefalse,而不会陷入undefined,这会将控制权从React 切换到开发人员,反之亦然。

              【讨论】:

                【解决方案16】:

                请试试这个代码

                import React from "react";
                
                class MyForm extends React.Component {
                    constructor(props) {
                        super(props);
                        this.state = { name: "" };
                        this.onFieldChange = this.onFieldChange.bind(this);
                    }
                
                    onFieldChange(e) {
                        this.setState({[e.target.name]: e.target.value});
                    }
                
                    render() {
                        return (
                            <form className="add-support-staff-form">
                                <input name="name" type="text" value={this.state.name} onChange={this.onFieldChange} />
                        </form>
                        );
                    }
                }
                
                export default MyForm;
                

                【讨论】:

                  【解决方案17】:

                  我相信我的输入是受控的,因为它有一个值。

                  现在您可以通过两种方式做到这一点,最好的方法是使用 1 onChange输入 设置一个 状态键 /em> 处理程序。如果您有复选框,则需要编写一个单独的 onChange 处理程序。

                  有了 Class 组件,你会想这样写?

                  
                  import React from 'react';
                  
                  export default class MyForm extends React.Component {
                      constructor(props) {
                          super(props);
                          this.state = {
                           myFormFields: {
                             name: '',
                             dob: '',
                             phone: ''
                           }
                          }
                  
                        this.onFormFieldChange = this.onFormFieldChange.bind(this)
                      }
                  
                  
                  // Always have your functions before your render to keep state batches in sync.
                      onFormFieldChange(e) {
                         // No need to return this function can be void
                         this.setState({
                           myFormFields: {
                              ...this.state.myFormFields,
                              [e.target.name]: e.target.value 
                           }
                         })
                      }
                  
                  
                      render() {
                         // Beauty of classes we can destruct our state making it easier to place
                         const { myFormFields } = this.state
                        
                          return (
                              <form className="add-support-staff-form">
                                  <input name="name" type="text" value={myFormFields.name} onChange={this.onFormFieldChange}/>
                                  <input name="dob" type="date" value={myFormFields.dob} onChange={this.onFormFieldChange}/>
                                  <input name="phone" type="number" value={myFormFields.phone} onChange={this.onFormFieldChange}/>
                              </form>
                          )
                      }
                  
                  }
                  
                  export default MyForm;
                  

                  希望对一个类有所帮助,但最有表现力以及开发人员推动每个人使用的最新东西是功能组件。这就是你想要引导的,因为类组件不能与最新的库很好地交织在一起,因为它们现在都使用自定义 hooks

                  写成函数式组件

                  
                  import React, { useState } from 'react';
                  
                  const MyForm = (props) => {
                      // Create form initial state
                      const [myFormFields, setFormFields] = useState({
                         name: '',
                         dob: '',
                         phone: ''
                      })
                  
                  
                     // Always have your functions before your return to keep state batches in sync.
                      const onFormFieldChange = (e) => {
                         // No need to return this function can be void
                         setFormFields({
                           ...myFormFields,
                           [e.target.name]: e.target.value 
                         })
                      }
                  
                        
                         return (
                              <form className="add-support-staff-form">
                                  <input name="name" type="text" value={myFormFields.name} onChange={onFormFieldChange}/>
                                  <input name="dob" type="date" value={myFormFields.dob} onChange={onFormFieldChange}/>
                                  <input name="phone" type="number" value={myFormFields.phone} onChange={onFormFieldChange}/>
                              </form>
                          )
                  
                  }
                  
                  export default MyForm;
                  

                  希望这会有所帮助! ?

                  【讨论】:

                  • 好哥们
                  【解决方案18】:

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

                  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>    
                    }
                  }
                  

                  【讨论】:

                    猜你喜欢
                    • 2019-10-22
                    • 2017-06-30
                    • 2020-11-04
                    • 1970-01-01
                    • 2022-08-02
                    • 1970-01-01
                    • 2020-08-04
                    • 2023-02-08
                    • 2019-04-03
                    相关资源
                    最近更新 更多