我有一个简单的反应组件,我相信它有一个受控输入的表单:
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
我相信我的输入是受控的,因为它具有价值。
对于要控制的输入,它的值必须对应于状态变量的值。
您的示例中最初未满足该条件,因为最初未设置 this.state.name
。因此,输入最初是不受控制的。一旦第一次触发 onChange
处理程序,就会设置 this.state.name
。此时,满足上述条件,认为输入受控。这种从不受控到受控的转变会产生上面看到的错误。
通过在构造函数中初始化 this.state.name
:
例如
this.state = { name: '' };
输入将从一开始就受到控制,从而解决问题。有关更多示例,请参阅 React Controlled Components。
与此错误无关,您应该只有一个默认导出。您上面的代码有两个。
当您第一次渲染您的组件时,this.state.name
未设置,因此它的计算结果为 undefined
或 null
,您最终将 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 };
将导致输入被控制。应该明白,重要的是价值来自哪里,而不是价值是什么。
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.
另一种方法可能是在您的输入中设置默认值,如下所示:
<input name="name" type="text" value={this.state.name || ''} onChange={this.onFieldChange('name').bind(this)}/>
我知道其他人已经回答了这个问题。但是这里有一个非常重要的因素可能会帮助其他遇到类似问题的人:
您必须在输入字段(例如 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
解决此问题的简单解决方案是默认设置一个空值:
<input name='myInput' value={this.state.myInput || ''} onChange={this.handleChange} />
在构造函数中将字段值设置为“”(空字符串)的一个潜在缺点是该字段是否是可选字段并且未编辑。除非您在发布表单之前进行一些按摩,否则该字段将作为空字符串而不是 NULL 持久保存到您的数据存储中。
这种替代方法将避免空字符串:
constructor(props) {
super(props);
this.state = {
name: null
}
}
...
<input name="name" type="text" value={this.state.name || ''}/>
就我而言,我错过了一些非常微不足道的东西。
<input value={state.myObject.inputValue} />
当我收到警告时,我的状态如下:
state = {
myObject: undefined
}
通过交替我的状态来引用我的值的输入,我的问题得到了解决:
state = {
myObject: {
inputValue: ''
}
}
当您在输入中使用 onChange={this.onFieldChange('name').bind(this)}
时,您必须将状态空字符串声明为属性字段的值。
不正确的方式:
this.state ={
fields: {},
errors: {},
disabled : false
}
正确方法:
this.state ={
fields: {
name:'',
email: '',
message: ''
},
errors: {},
disabled : false
}
如果您的组件上的道具作为状态传递,请为您的输入标签设置一个默认值
<input type="text" placeholder={object.property} value={object.property ? object.property : ""}>
在初始状态下将值设置为“名称”属性。
this.state={ 名称:''};
对此的更新。对于 React Hooks,使用 const [name, setName] = useState(" ")
" "
而不是 ""
?这会导致您丢失任何提示文本,并且如果您单击并键入,您会在输入任何数据之前得到一个偷偷摸摸的空格。
如果 this.state.name 为空,只需创建一个回退到 ''。
<input name="name" type="text" value={this.state.name || ''} onChange={this.onFieldChange('name').bind(this)}/>
这也适用于 useState 变量。
undefined
作为我所在州的值。投反对票的人可以解释原因吗?在页面的下方,有与此相同的响应,但有 55 多个赞成票。
我相信我的输入是受控的,因为它具有价值。
现在您可以通过两种方式执行此操作,最好的方法是使用 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;
希望这可以帮助! 😎
在我的情况下,组件正在重新渲染并抛出 A component is changing an uncontrolled input of type checkbox to be controlled
错误。事实证明,这种行为是由于未将 true
或 false
保持为复选框选中状态(有时我得到 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]);
这使得所有复选框都保持其状态为 true
或 false
而不会落入 undefined
,这会将控制从 React 切换到开发人员,反之亦然。
对于使用 Formik
的人,您需要将特定字段 name
的默认值添加到表单的 initialValues
。
我有同样的问题。问题是当我将状态信息保留为空白时
const [name, setName] = useState()
我通过像这样添加空字符串来修复它
const [name, setName] = useState('')
这通常只发生在应用程序启动时您没有控制字段的值并且在某些事件或某些函数被触发或状态更改之后,您现在正试图控制输入字段中的值。
这种无法控制输入然后控制它的转变是导致问题首先发生的原因。
避免这种情况的最好方法是在组件的构造函数中为输入声明一些值。这样输入元素从应用程序开始就具有价值。
请尝试此代码
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;
简而言之,如果您使用的是类组件,则必须使用状态初始化输入,如下所示:
this.state = { the_name_attribute_of_the_input: "initial_value_or_empty_value" };
并且您必须对所有要在代码中更改其值的输入执行此操作。
在使用功能组件的情况下,您将使用钩子来管理输入值,并且您必须为以后要操作的每个输入设置初始值,如下所示:
const [name, setName] = React.useState({name: 'initialValue'});
如果你不想有初始值,你可以放一个空字符串。
为了动态设置表单输入的状态属性并保持它们受控,您可以执行以下操作:
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>
}
}
name={'question_groups.${questionItem.id}'}
?value
属性具有非空值/未定义值,则输入是受控的。 属性不需要对应于状态变量(它可以只是一个常量,组件仍然可以被认为是受控的)。亚当的回答更正确,应该被接受。