React - 改变不受控制的输入
React - changing an uncontrolled input
我有一个简单的 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;
当我 运行 我的应用程序时,我收到以下警告:
Warning: MyForm is changing an uncontrolled input of type text to be
controlled. Input elements should not switch from uncontrolled to
controlled (or vice versa). Decide between using a controlled or
uncontrolled input element for the lifetime of the component
我相信我的输入是受控的,因为它有价值。我想知道我做错了什么?
我正在使用 React 15.1.0
I believe my input is controlled since it has a value.
要控制输入,其值必须对应于状态变量的值。
您的示例最初不满足该条件,因为 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
(注意它是 !=
,而不是 !==
),并且由于 undefined == null
在 JavaScript,它决定它是不受控制的。
因此,当 onFieldChange()
被调用时,this.state.name
被设置为一个字符串值,您的输入从不受控制变为受控。
如果您在构造函数中执行 this.state = {name: ''}
,因为 '' != null
,您的输入将始终有一个值,并且该消息将消失。
另一种方法是在输入中设置默认值,如下所示:
<input name="name" type="text" value={this.state.name || ''} onChange={this.onFieldChange('name').bind(this)}/>
我知道其他人已经回答了这个问题。但这里有一个非常重要的因素可能会帮助其他遇到类似问题的人:
您必须在输入字段(例如文本字段、复选框、单选框等)中添加一个 onChange
处理程序。始终通过 onChange
处理程序处理 activity。
示例:
<input ... onChange={ this.myChangeHandler} ... />
当您使用 checkbox 时,您可能需要使用 !!
.
处理它的 checked
状态
示例:
<input type="checkbox" checked={!!this.state.someValue} onChange={.....} >
Reference: https://github.com/facebook/react/issues/6779#issuecomment-326314716
如果组件上的道具作为状态传递,请为输入标签设置默认值
<input type="text" placeholder={object.property} value={object.property ? object.property : ""}>
在初始状态下将值设置为 'name' 属性。
this.state={ name:''};
在构造函数中将字段值设置为“”(空字符串)的一个潜在缺点是该字段是否为可选字段且未被编辑。除非您在发布表单之前进行一些修改,否则该字段将作为空字符串而不是 NULL 保存到您的数据存储中。
此替代方法将避免空字符串:
constructor(props) {
super(props);
this.state = {
name: null
}
}
...
<input name="name" type="text" value={this.state.name || ''}/>
当您在输入中使用 onChange={this.onFieldChange('name').bind(this)}
时,您必须将状态空字符串声明为 属性 字段的值。
错误的方式:
this.state ={
fields: {},
errors: {},
disabled : false
}
正确方法:
this.state ={
fields: {
name:'',
email: '',
message: ''
},
errors: {},
disabled : false
}
就我而言,我遗漏了一些非常微不足道的东西。
<input value={state.myObject.inputValue} />
收到警告时我的状态如下:
state = {
myObject: undefined
}
通过交替我的状态引用我的值的输入,我的问题得到解决:
state = {
myObject: {
inputValue: ''
}
}
这通常只发生在应用程序启动时您没有控制字段的值,并且在某些事件或某些函数触发或状态更改后,您现在试图控制输入字段中的值。
这种从无法控制输入到控制输入的转变是导致问题发生的首要原因。
避免这种情况的最佳方法是在组件的构造函数中为输入声明一些值。
这样输入元素从应用程序开始就具有价值。
解决此问题的简单方法是默认设置一个空值:
<input name='myInput' value={this.state.myInput || ''} onChange={this.handleChange} />
对此的更新。对于 React Hooks 使用 const [name, setName] = useState(" ")
要动态设置表单输入的状态属性并控制它们,您可以这样做:
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>
}
}
如果 this.state.name 为空,则简单地创建一个回退到 ''。
<input name="name" type="text" value={this.state.name || ''} onChange={this.onFieldChange('name').bind(this)}/>
这也适用于 useState 变量。
在我的案例中,组件重新渲染并抛出 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
.
请试试这个代码
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;
I believe my input is controlled since it has a value.
现在你可以通过两种方式做到这一点,最好的方法是为每个 输入 和 设置一个 状态键 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;
希望对 class 有所帮助,但开发人员推动每个人使用的最具性能和最新的东西是 功能组件 。这是你想要引导的,因为 class 组件不能很好地与最新的库交织在一起,因为它们现在都使用自定义 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;
希望对您有所帮助!
我遇到了同样的问题。
问题是当我将状态信息留空时
const [name, setName] = useState()
我通过像这样添加空字符串来修复它
const [name, setName] = useState('')
简而言之,如果您使用 class 组件,则必须使用状态初始化输入,如下所示:
this.state = { the_name_attribute_of_the_input: "initial_value_or_empty_value" };
并且您必须对所有要在代码中更改其值的输入执行此操作。
在使用函数式组件的情况下,您将使用钩子来管理输入值,并且您必须为您稍后要操作的每个输入设置初始值,如下所示:
const [name, setName] = React.useState({name: 'initialValue'});
如果你不想有初始值,你可以输入一个空字符串。
我有一个简单的 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;
当我 运行 我的应用程序时,我收到以下警告:
Warning: MyForm is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component
我相信我的输入是受控的,因为它有价值。我想知道我做错了什么?
我正在使用 React 15.1.0
I believe my input is controlled since it has a value.
要控制输入,其值必须对应于状态变量的值。
您的示例最初不满足该条件,因为 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
(注意它是 !=
,而不是 !==
),并且由于 undefined == null
在 JavaScript,它决定它是不受控制的。
因此,当 onFieldChange()
被调用时,this.state.name
被设置为一个字符串值,您的输入从不受控制变为受控。
如果您在构造函数中执行 this.state = {name: ''}
,因为 '' != null
,您的输入将始终有一个值,并且该消息将消失。
另一种方法是在输入中设置默认值,如下所示:
<input name="name" type="text" value={this.state.name || ''} onChange={this.onFieldChange('name').bind(this)}/>
我知道其他人已经回答了这个问题。但这里有一个非常重要的因素可能会帮助其他遇到类似问题的人:
您必须在输入字段(例如文本字段、复选框、单选框等)中添加一个 onChange
处理程序。始终通过 onChange
处理程序处理 activity。
示例:
<input ... onChange={ this.myChangeHandler} ... />
当您使用 checkbox 时,您可能需要使用 !!
.
checked
状态
示例:
<input type="checkbox" checked={!!this.state.someValue} onChange={.....} >
Reference: https://github.com/facebook/react/issues/6779#issuecomment-326314716
如果组件上的道具作为状态传递,请为输入标签设置默认值
<input type="text" placeholder={object.property} value={object.property ? object.property : ""}>
在初始状态下将值设置为 'name' 属性。
this.state={ name:''};
在构造函数中将字段值设置为“”(空字符串)的一个潜在缺点是该字段是否为可选字段且未被编辑。除非您在发布表单之前进行一些修改,否则该字段将作为空字符串而不是 NULL 保存到您的数据存储中。
此替代方法将避免空字符串:
constructor(props) {
super(props);
this.state = {
name: null
}
}
...
<input name="name" type="text" value={this.state.name || ''}/>
当您在输入中使用 onChange={this.onFieldChange('name').bind(this)}
时,您必须将状态空字符串声明为 属性 字段的值。
错误的方式:
this.state ={
fields: {},
errors: {},
disabled : false
}
正确方法:
this.state ={
fields: {
name:'',
email: '',
message: ''
},
errors: {},
disabled : false
}
就我而言,我遗漏了一些非常微不足道的东西。
<input value={state.myObject.inputValue} />
收到警告时我的状态如下:
state = {
myObject: undefined
}
通过交替我的状态引用我的值的输入,我的问题得到解决:
state = {
myObject: {
inputValue: ''
}
}
这通常只发生在应用程序启动时您没有控制字段的值,并且在某些事件或某些函数触发或状态更改后,您现在试图控制输入字段中的值。
这种从无法控制输入到控制输入的转变是导致问题发生的首要原因。
避免这种情况的最佳方法是在组件的构造函数中为输入声明一些值。 这样输入元素从应用程序开始就具有价值。
解决此问题的简单方法是默认设置一个空值:
<input name='myInput' value={this.state.myInput || ''} onChange={this.handleChange} />
对此的更新。对于 React Hooks 使用 const [name, setName] = useState(" ")
要动态设置表单输入的状态属性并控制它们,您可以这样做:
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>
}
}
如果 this.state.name 为空,则简单地创建一个回退到 ''。
<input name="name" type="text" value={this.state.name || ''} onChange={this.onFieldChange('name').bind(this)}/>
这也适用于 useState 变量。
在我的案例中,组件重新渲染并抛出 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
.
请试试这个代码
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;
I believe my input is controlled since it has a value.
现在你可以通过两种方式做到这一点,最好的方法是为每个 输入 和 设置一个 状态键 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;
希望对 class 有所帮助,但开发人员推动每个人使用的最具性能和最新的东西是 功能组件 。这是你想要引导的,因为 class 组件不能很好地与最新的库交织在一起,因为它们现在都使用自定义 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;
希望对您有所帮助!
我遇到了同样的问题。 问题是当我将状态信息留空时
const [name, setName] = useState()
我通过像这样添加空字符串来修复它
const [name, setName] = useState('')
简而言之,如果您使用 class 组件,则必须使用状态初始化输入,如下所示:
this.state = { the_name_attribute_of_the_input: "initial_value_or_empty_value" };
并且您必须对所有要在代码中更改其值的输入执行此操作。
在使用函数式组件的情况下,您将使用钩子来管理输入值,并且您必须为您稍后要操作的每个输入设置初始值,如下所示:
const [name, setName] = React.useState({name: 'initialValue'});
如果你不想有初始值,你可以输入一个空字符串。