反应-改变一个不受控制的输入

我有一个简单的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

421782 次浏览

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

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

在你的例子中,这个条件最初没有被满足,因为this.state.name最初没有被设置。因此,输入最初是不受控制的。一旦onChange处理程序第一次被触发,this.state.name就会被设置。此时,上述条件满足,则认为输入是受控的。这种从不受控到受控的转变产生了上面所见的错误。

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

如。

this.state = { name: '' };

输入将从一开始就受到控制,从而解决问题。更多示例参见React控制组件

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

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

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

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

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

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

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

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

你必须在你的输入字段中添加onChange处理程序(例如textField,复选框,radio等)。始终通过onChange处理程序处理活动。

例子:

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

当你使用复选框时,你可能需要用!!来处理它的checked状态。

例子:

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

参考: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: ''
}
}

这种情况通常只发生在应用程序启动时,您没有控制字段的值,而在某些事件或某些函数触发或状态改变后,您现在试图控制输入字段中的值。

这种从无法控制输入到可以控制输入的转变是导致问题发生的首要原因。

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

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

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

只需创建一个回退到“if 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错误。事实证明,这种行为是没有为复选框选中状态保留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切换到开发人员,反之亦然。

对于使用Formik的人,你需要在表单的initialValues中添加特定字段name的默认值。

请试试这段代码

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;

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

现在你可以用两种方法做到这一点,最好的方法是用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;

希望这能有所帮助!😎

我也有同样的问题。 问题是当我保持状态信息空白

  const [name, setName] = useState()

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

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

简而言之,如果你正在使用类组件,你必须使用状态初始化输入,就像这样:

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

你必须对所有的输入都这么做你想在代码中改变它们的值。

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

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

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

在我的情况下,在设置状态时出现拼写错误,导致定义值未定义。

这是value的默认状态

const [email, setEmail] = useState(true);

我的错误是

setEmail(res?.data?.notificationSetting.is_email_notificatio);

解是-

setEmail(res?.data?.notificationSetting.is_email_notification);

最后一个char丢失了