【问题标题】:A component is changing an uncontrolled input of type text to be controlled error in ReactJS一个组件正在将文本类型的不受控制的输入更改为 ReactJS 中的受控错误
【发布时间】:2018-04-11 06:01:10
【问题描述】:

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

以下是我的代码:

constructor(props) {
  super(props);
  this.state = {
    fields: {},
    errors: {}
  }
  this.onSubmit = this.onSubmit.bind(this);
}

....

onChange(field, e){
  let fields = this.state.fields;
  fields[field] = e.target.value;
  this.setState({fields});
}

....

render() {
  return(
    <div className="form-group">
      <input
        value={this.state.fields["name"]}
        onChange={this.onChange.bind(this, "name")}
        className="form-control"
        type="text"
        refs="name"
        placeholder="Name *"
      />
      <span style={{color: "red"}}>{this.state.errors["name"]}</span>
    </div>
  )
}

【问题讨论】:

  • 状态中fields的初始值是多少?
  • 构造函数(props) { super(props); this.state = { 字段:{},错误:{} } this.onSubmit = this.onSubmit.bind(this); }
  • 我刚刚发现您可以使用useRef 有条件地将值设置为当前输入,例如value={amountInputFocused ? amountRef.current?.value : amountState}。不确定这是否是设计使然,但它可以工作,并且可以消除错误。

标签: javascript reactjs


【解决方案1】:

解决此问题的最佳方法是将初始状态设置为 ''

constructor(props) {
 super(props)
  this.state = {
    fields: {
      first_name: ''
    }
  }
  this.onChange = this.onChange.bind(this);
}

onChange(e) {
  this.setState({
    fields:{
     ...this.state.fields,
     [e.target.name]: e.target.value
    }
  })
}


render() {
  return(
    <div className="form-group">
      <input
        value={this.state.fields.first_name}
        onChange={this.onChange}
        className="form-control"
        name="first_name" // Same as state key
        type="text"
        refs="name"
        placeholder="Name *"
      />
      <span style={{color: "red"}}>{this.state.errors.first_name}</span>
    </div>
  )
} 

然后,您仍然可以像 if (field) 一样运行检查,并且如果您的值为 '',仍然可以获得相同的结果。

现在,因为您的值在评估后现在被归类为 type string 而不是 undefined。因此,从控制台清除一个大红色块的错误??。

【讨论】:

    【解决方案2】:

    如上所述,您需要设置初始状态,在我的情况下,我忘记在 setSate(); 中添加 ' ' 引号;

      const AddUser = (props) => {
      const [enteredUsername, setEnteredUsername] = useState()
      const [enteredAge, setEnteredAge] = useState()
    

    出现以下错误

    正确的代码是简单地将初始状态设置为空字符串''

      const AddUser = (props) => {
      const [enteredUsername, setEnteredUsername] = useState('')
      const [enteredAge, setEnteredAge] = useState('')
    

    【讨论】:

    • 正是那个错误!!非常感谢。
    • 我遇到了同样的问题,只是我没有使用空字符串作为初始值,而是使用undefined。切换到一个空字符串解决了这个问题。谢谢
    【解决方案3】:

    我在使用 react hooks 时遇到了同样的警告, 虽然我之前已经将初始状态初始化为:-

    const [post,setPost] = useState({title:"",body:""})

    但后来我在 onChange 事件处理程序上覆盖了预定义状态对象的一部分,

     const onChange=(e)=>{
            setPost({[e.target.name]:e.target.value})
        }
    

    解决方案 我通过首先处理前一个状态的整个对象(通过使用扩展运算符)然后在它之上进行编辑来解决这个问题,

     const onChange=(e)=>{
            setPost({...post,[e.target.name]:e.target.value})
        }
    

    【讨论】:

      【解决方案4】:

      喜欢这个

      value={this.state.fields && this.state.fields["name"] || ''}
      

      为我工作。

      但我这样设置初始状态:

      this.state = {
        fields: [],
      }
      

      【讨论】:

        【解决方案5】:

        改变这个

          const [values, setValues] = useState({intialStateValues});
        

        为此

          const [values, setValues] = useState(intialStateValues);
        

        【讨论】:

          【解决方案6】:

          当输入字段值未定义时出现此问题的原因然后从反应中抛出警告。如果您为多个输入字段创建一个 changeHandler 并且您想使用 changeHandler 更改状态,那么您需要使用扩展运算符分配先前的值。就像我的代码一样。

          constructor(props){
              super(props)
              this.state = {
                  user:{
                      email:'',
                      password:''
                  }
              }
          }
          
          // This handler work for every input field
          changeHandler = event=>{
              // Dynamically Update State when change input value
              this.setState({
                  user:{
                      ...this.state.user,
                      [event.target.name]:event.target.value
                  }
              })
          }
          
          submitHandler = event=>{
              event.preventDefault()
          
              // Your Code Here...
          }
          
          render(){
              return (
                  <div className="mt-5">
                 
                      <form onSubmit={this.submitHandler}>
                          <input type="text" value={this.state.user.email} name="email" onChage={this.changeHandler} />
                          
                          <input type="password" value={this.state.user.password} name="password" onChage={this.changeHandler} />
          
                          <button type="submit">Login</button>
                      </form>
                
          
                  </div>
              )
          }
          

          【讨论】:

            【解决方案7】:

            如果值不存在或为空,则放置空值。

            value={ this.state.value || "" }
            

            【讨论】:

            • 谢谢麦克斯!这是问题的原因,但是,您的代码 sn-p 将无法按原样适用于该用户(this.state.fields.name vs this.state.value
            【解决方案8】:

            可以应用多种方法:

            • 基于类的方法:使用本地状态并使用默认值定义现有字段:
            constructor(props) {
                super(props);
                this.state = {
                  value:''
                }
              }
            <input type='text'
                              name='firstName'
                              value={this.state.value}
                              className="col-12"
                              onChange={this.onChange}
                              placeholder='Enter First name' />
            
            
            • 在函数式组件中使用 Hooks React > 16.8:
            [value, setValue] = useState('');
            <input type='text'
                              name='firstName'
                              value={value}
                              className="col-12"
                              onChange={this.onChange}
                              placeholder='Enter First name' />
            
            
            • 如果使用 propTypes 并为函数样式的 HOC 组件提供 propTypes 的默认值。
             HOC.propTypes = {
                value       : PropTypes.string
              }
              HOC.efaultProps = {
                value: ''
              }
            
            function HOC (){
            
              return (<input type='text'
                              name='firstName'
                              value={this.props.value}
                              className="col-12"
                              onChange={this.onChange}
                              placeholder='Enter First name' />)
            
            }
            
            
            

            【讨论】:

              【解决方案9】:

              使用 React Hooks 也不要忘记设置初始值。
              我使用的是&lt;input type='datetime-local' value={eventStart} /&gt;,最初的eventStart 就像

              const [eventStart, setEventStart] = useState();
              而是
              const [eventStart, setEventStart] = useState('');

              括号中的空字符串是不同的。
              此外,如果您像我一样在提交后重置表单,您需要再次将其设置为空字符串,而不仅仅是空括号。

              这只是我对这个话题的一点贡献,也许它会对某人有所帮助。

              【讨论】:

                【解决方案10】:

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

                解决方案:检查值是否未定义

                React / Formik / Bootstrap / TypeScript

                例子:

                { values?.purchaseObligation.remainingYear ?
                  <Input
                   tag={Field}
                   name="purchaseObligation.remainingYear"
                   type="text"
                   component="input"
                  /> : null
                }
                

                【讨论】:

                  【解决方案11】:

                  除了接受的答案之外,如果您使用input 类型为checkboxradio,我发现我还需要检查checked 属性是否为空/未定义。

                  <input
                    id={myId}
                    name={myName}
                    type="checkbox" // or "radio"
                    value={myStateValue || ''}
                    checked={someBoolean ? someBoolean : false}
                    />
                  

                  如果您使用的是 TS(或 Babel),则可以使用空值合并而不是逻辑 OR 运算符:

                  value={myStateValue ?? ''}
                  checked={someBoolean ?? false}
                  

                  【讨论】:

                  • 用简单的思维解决复杂的事情,酷@Lynden Noye
                  【解决方案12】:

                  如果您在 on 字段中使用多个输入,请遵循: 例如:

                  class AddUser extends React.Component {
                     constructor(props){
                       super(props);
                  
                       this.state = {
                         fields: { UserName: '', Password: '' }
                       };
                     }
                  
                     onChangeField = event => {
                      let name = event.target.name;
                      let value = event.target.value;
                      this.setState(prevState => {
                          prevState.fields[name] =  value;
                          return {
                             fields: prevState.fields
                          };
                      });
                    };
                  
                    render() { 
                       const { UserName, Password } = this.state.fields;
                       return (
                           <form>
                               <div>
                                   <label htmlFor="UserName">UserName</label>
                                   <input type="text" 
                                          id='UserName' 
                                          name='UserName'
                                          value={UserName}
                                          onChange={this.onChangeField}/>
                                </div>
                                <div>
                                    <label htmlFor="Password">Password</label>
                                    <input type="password" 
                                           id='Password' 
                                           name='Password'
                                           value={Password}
                                           onChange={this.onChangeField}/>
                                </div>
                           </form>
                       ); 
                    }
                  }
                  

                  在以下位置搜索您的问题:

                  onChangeField = event => {
                      let name = event.target.name;
                      let value = event.target.value;
                      this.setState(prevState => {
                          prevState.fields[name] =  value;
                          return {
                              fields: prevState.fields
                          };
                      });
                  };
                  

                  【讨论】:

                    【解决方案13】:

                    就我而言,这几乎就是 Mayank Shukla 的最佳答案所说的。唯一的细节是我的状态完全没有我定义的属性。

                    例如,如果你有这个状态:

                    state = {
                        "a" : "A",
                        "b" : "B",
                    }
                    

                    如果你正在扩展你的代码,你可能想要添加一个新的道具,所以,你可以在代码的其他地方创建一个新属性c,其值不仅在组件的状态上未定义,而且属性 本身是未定义的。

                    要解决这个问题,只需确保将 c 添加到您的状态中并给它一个适当的初始值。

                    例如,

                    state = {
                        "a" : "A",
                        "b" : "B",
                        "c" : "C", // added and initialized property!
                    }
                    

                    希望我能够解释我的极端情况。

                    【讨论】:

                      【解决方案14】:
                      const [name, setName] = useState()
                      

                      在您输入文本字段时立即生成错误

                      const [name, setName] = useState('') // <-- by putting in quotes 
                      

                      将解决此字符串示例的问题。

                      【讨论】:

                        【解决方案15】:

                        value 更改为defaultValue 即可解决此问题。

                        注意

                        defaultValue 仅用于初始加载。 如果你想初始化input那么你应该使用defaultValue,但是如果你想使用state来改变值那么你需要使用value。阅读this了解更多信息。

                        我在input 中使用了value={this.state.input ||""} 来消除该警告。

                        【讨论】:

                        • 谢谢!我遇到了这个问题,其他解决方案不适用于我,但这个解决方案有帮助。
                        【解决方案16】:

                        在组件内部按如下方式放置输入框。

                        <input className="class-name"
                                      type= "text"
                                      id="id-123"
                                      value={ this.state.value || "" }
                                      name="field-name"
                                      placeholder="Enter Name"
                                      />
                        

                        【讨论】:

                          【解决方案17】:

                          原因是,在您定义的状态下:

                          this.state = { fields: {} }
                          

                          字段作为空白对象,因此在第一次渲染期间this.state.fields.name 将是undefined,输入字段将获得其值:

                          value={undefined}
                          

                          因此,输入字段将变得不受控制。

                          在输入中输入任何值后,fields 的状态将更改为:

                          this.state = { fields: {name: 'xyz'} }
                          

                          此时输入字段被转换为受控组件;这就是您收到错误的原因:

                          组件正在将文本类型的不受控制的输入更改为 控制。

                          可能的解决方案:

                          1- 将状态下的fields 定义为:

                          this.state = { fields: {name: ''} }
                          

                          2- 或者像这样使用Short-circuit evaluation 定义值属性:

                          value={this.state.fields.name || ''}   // (undefined || '') = ''
                          

                          【讨论】:

                          • 是否有一个原因 undefined 是“不受控制的”而后者是“受控的” - 这些是什么意思?
                          • 因为'' 是一个有效的字符串值。因此,当您将'' 更改为“xyz”时,输入类型不会更改为受控方式。但是在undefinedxyz 的情况下,类型将从不受控制变为受控。
                          • 谢谢你这对我有用:value={this.state.fields.name || ''}
                          • 我不知道一旦值变得未定义,输入就会自动不受控制,太好了。已经解决了我的问题
                          • 太棒了!因为我没有在官方文档中看到这一点,还是我瞎了?请链接到来源:)
                          【解决方案18】:

                          简单地说,您必须先设置初始状态

                          如果您不设置 初始状态,react 会将其视为不受控制的组件

                          【讨论】:

                            【解决方案19】:

                            首先设置当前状态...this.state

                            这是因为当您要分配一个新状态时,它可能是未定义的。所以它也将通过设置状态提取当前状态来修复

                            this.setState({...this.state, field})
                            

                            如果你的状态中有一个对象,你应该如下设置状态, 假设您必须在用户对象中设置用户名。

                            this.setState({user:{...this.state.user, ['username']: username}})
                            

                            【讨论】:

                            • 据我了解,setState 已经只覆盖了字段,所以在第一种情况下应该不需要解构分配。在第二种状态下,它可能是必要的,因为 setState 将替换一个子对象批发。
                            猜你喜欢
                            • 2019-11-16
                            • 1970-01-01
                            • 2021-05-21
                            • 2020-11-04
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 2019-08-21
                            相关资源
                            最近更新 更多