ChatGPT解决这个技术问题 Extra ChatGPT

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


f
fvgs

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

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

您的示例中最初未满足该条件,因为最初未设置 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 的真实性
M
Mohamad Shiralizadeh

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

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

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

我认为这也可以
非常感谢,这正是我正在寻找的。我应该记住使用这种模式有什么缺点或潜在问题吗?
这对我有用,因为字段是从 API 动态呈现的,所以在安装组件时我不知道字段的名称。这是一种享受!
@JpCrow 好答案。您可以在此处阅读有关此内容的更多信息stackoverflow.com/a/476445/3850405 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
G
Geo

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

您必须在输入字段(例如 textField、checkbox、radio 等)中添加一个 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% 同意这一点,初始状态是 {},所以检查的值将是未定义的并使其不受控制,
双爆炸解决方案工作得很好,谢谢!
这对我有帮助。谢谢你。
如果我们从 API 获取数据并将其显示在 TextField 中会怎样?
M
Mustapha GHLISSI

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

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

荣幸 :)
G
Greg R Taylor

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

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

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

... 

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

M
Menelaos Kotsollaris

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

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

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

state = {
   myObject: undefined
}

通过交替我的状态来引用我的值的输入,我的问题得到了解决:

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

感谢您帮助我了解我遇到的真正问题
K
KARTHIKEYAN.A

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

不正确的方式:

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

正确方法:

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

E
Emperor Krauser

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

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

l
lakmal_sathyajith

在初始状态下将值设置为“名称”属性。

this.state={ 名称:''};


k
kboul

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


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

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

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

这也适用于 useState 变量。


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

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

现在您可以通过两种方式执行此操作,最好的方法是使用 1 个 onChange 处理程序为每个输入设置一个状态键。如果您有复选框,则需要编写单独的 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;

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

编写为功能组件


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;

希望这可以帮助! 😎


优秀的伙伴
M
Marecky

在我的情况下,组件正在重新渲染并抛出 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 切换到开发人员,反之亦然。


G
Guillaume Munsch

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


你最好举个例子。很难理解。
@DavidPiao 这对我来说很有意义,你能详细说明一下吗?
S
Shuhad zaman

我有同样的问题。问题是当我将状态信息保留为空白时

  const [name, setName] = useState()

我通过像这样添加空字符串来修复它

  const [name, setName] = useState('')

谢谢你,先生!这是我的问题并修复
A
Ashish Singh

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

这种无法控制输入然后控制它的转变是导致问题首先发生的原因。

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


T
Tikam Chand

请尝试此代码

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;

A
Abdulrazak Zakieh

简而言之,如果您使用的是类组件,则必须使用状态初始化输入,如下所示:

this.state = { the_name_attribute_of_the_input: "initial_value_or_empty_value" };

并且您必须对所有要在代码中更改其值的输入执行此操作。

在使用功能组件的情况下,您将使用钩子来管理输入值,并且您必须为以后要操作的每个输入设置初始值,如下所示:

const [name, setName] = React.useState({name: 'initialValue'});

如果你不想有初始值,你可以放一个空字符串。


M
Mark

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

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