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

时间:2016-05-25 03:50:59

标签: javascript node.js reactjs react-state

我有一个简单的反应组件,我认为它具有一个受控输入的形式:

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

15 个答案:

答案 0 :(得分:342)

  

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

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

在您的示例中最初未满足该条件,因为最初未设置this.state.name。因此,输入最初是不受控制的。首次触发onChange处理程序后,this.state.name将被设置。此时,满足上述条件并且认为输入受到控制。这种从不受控制到受控制的转变产生了上述错误。

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

e.g。

this.state = { name: '' };

输入将从一开始就被控制,解决问题。有关更多示例,请参阅React Controlled Components

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

答案 1 :(得分:91)

首次渲染组件时,this.state.name未设置,因此评估为undefined,最后将value={undefined}传递给input

当ReactDOM检查字段是否被控制时,it checks to see if value != null(请注意它是!=,而不是!==),以及自JavaScript undefined == null以来,它决定这是不受控制的。

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

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

答案 2 :(得分:35)

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

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

答案 3 :(得分:6)

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

您必须在输入字段中添加onChange处理程序(例如textField,checkbox,radio等)。并始终通过onChange处理程序处理活动,例如:

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

当您使用复选框时,您可能需要使用checked来处理其!!州:

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

参考:https://github.com/facebook/react/issues/6779#issuecomment-326314716

答案 4 :(得分:5)

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

此替代方法将避免使用空字符串:

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

... 

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

答案 5 :(得分:3)

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

错误的方式:

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

正确的方法:

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

答案 6 :(得分:2)

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

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

答案 7 :(得分:2)

就我而言,我确实缺少了一些琐碎的东西。

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

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

state = {
   myObject: undefined
}

通过更改状态以引用我的值输入,我的问题已解决:

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

答案 8 :(得分:1)

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

this.state={ name:''};

答案 9 :(得分:0)

通常仅当在应用程序启动时您不控制文件的值并且在某些事件或某些函数被触发或状态改变后,现在尝试在输入字段中控制值时,才会发生这种情况。

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

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

答案 10 :(得分:0)

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

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

答案 11 :(得分:0)

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

答案 12 :(得分:0)

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

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

答案 13 :(得分:0)

就我而言,组件正在重新渲染并抛出 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 切换到开发者,反之亦然。

答案 14 :(得分:-2)

如果this.state.name为null,则仅创建对''的后备广告。

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

这也适用于useState变量。