反応-制御されていない入力の変更


360

私は、1つの制御された入力があると私が信じる形式の単純な反応コンポーネントを持っています:

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を使用しています

回答:


510

入力には値があるため、入力は制御されていると思います。

入力を制御するには、その値が状態変数の値に対応している必要があります。

this.state.name最初に設定されていないため、この条件は最初の例では満たされていません。したがって、入力は最初は制御されていません。いったんonChangeハンドラが最初にトリガされ、this.state.name設定されます。その時点で、上記の条件が満たされ、入力は制御されていると見なされます。制御されていない状態から制御されている状態へのこの移行により、上記のエラーが発生します。

コンストラクタthis.state.nameで初期化することにより:

例えば

this.state = { name: '' };

入力は最初から制御され、問題が修正されます。その他の例については、React制御コンポーネントを参照してください。

このエラーとは関係なく、デフォルトのエクスポートは1つだけです。上記のコードには2つあります。


7
答えを読んで何度もアイデアを理解することは困難ですが、この答えは、物語を伝え、視聴者に同時に理解させるのに最適な方法です。回答レベルの神!
surajnew55

2
ループ内に動的フィールドがある場合はどうなりますか?たとえば、フィールド名を次のように設定しますname={'question_groups.${questionItem.id}'}か?
user3574492 2018

2
動的フィールドはどのように機能しますか。フォームを動的に生成してから状態内のオブジェクトにフィールド名を設定していますが、最初に状態のフィールド名を最初に手動で設定してからオブジェクトに転送する必要がありますか?
ジョセフ

13
この答えは少し間違っています。プロップにnull以外/未定義の値がある場合、value入力が制御されます。小道具は状態変数に対応する必要はありません(それは単に定数である可能性があり、コンポーネントは制御されていると見なされます)。Adamの答えはより正確であり、受け入れられるべきです。
ecraig12345

2
うん-これは技術的に間違った答えです(しかし役に立ちます)。これに関するメモ-ラジオボタン(「値」の制御が少し異なる)を処理している場合、コンポーネントが(現在)制御されている場合でも、この反応警告は実際にスローされます。 github.com/facebook/react/issues/6779、そして!! isCheckedの真実性へ
mheavers

123

最初にコンポーネントをレンダリングするとき、this.state.nameは設定されていないため、undefinedまたはに評価されnull、最終的にvalue={undefined}または value={null}に渡されますinput

フィールドが制御されている場合ReactDOMのかどうかを確認した場合、かどうかを確認するために、それをチェックしvalue != null(それはだことに注意してください!=、ではない!==)、そして以来、undefined == nullJavaScriptで、それが制御不能だと判断しました。

したがって、onFieldChange()が呼び出さthis.state.nameれ、文字列値に設定されると、入力は制御されない状態から制御される状態になります。

this.state = {name: ''}コンストラクタで行うと、ので'' != null、入力は常に値を持ち、そのメッセージは消えます。


5
それだけの価値があるのですが、同じことはで発生する可能性this.props.<whatever>があります。ありがとう、アダム!
Don

うん!これはrender()、で定義した計算変数、またはタグ自体の式(評価結果が)を渡した場合にも発生する可能性がありますundefined。お役に立てて嬉しいです!
Leigh Brenecki 16

1
この回答に感謝します:受け入れられたものではありませんが、単に「を指定するname」よりもはるかによく問題を説明します。
machineghost 2016

1
制御入力がどのように機能するかを説明するために、回答を更新しました。ここで重要なのundefinedは、最初にの値が渡されるということではないことに注意してください。むしろ、this.state.name入力を制御不能にするのは状態変数として存在しないという事実です。たとえば、this.state = { name: undefined };その結果、入力が制御されます。重要なのは、価値が何であるかではなく、どこから来るかということです。
fvgs 2017年

1
@fvgsを使用this.state = { name: undefined }しても、制御されない入力が発生します。<input value={this.state.name} />への脱糖React.createElement('input', {value: this.state.name})。オブジェクトの存在しないプロパティにアクセスするとが返されるためundefined、これはまったく同じ関数React.createElement('input', {value: undefined})呼び出しnameとして評価されます— が設定されていないか、明示的にに設定されているかにかかわらずundefined、Reactは同じように動作します。この動作は、このJSFiddleで確認できます。
Leigh Brenecki 2017年

52

別のアプローチとして、次のように入力内にデフォルト値を設定することもできます。

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

1
<input name = "name" type = "text" defaultValue = "" onChange = {this.onFieldChange( 'name')。bind(this)} />これも機能すると思います
ガンダルフ'15年

本当にありがとう、これはまさに私が探していたものでした。このパターンを使用する際に留意すべき不利な点や潜在的な問題はありますか?
Marcel Otten 2018年

これは私にとってはうまくいきました。フィールドはAPIから動的にレンダリングされるため、コンポーネントがマウントされたときにフィールドの名前がわかりません。これはごちそうでした!
ジェイミー-Fenrir Digital Ltd

18

他の人がすでにこれに答えていることは知っています。しかし、同様の問題を経験している他の人々を助けるかもしれないここでの非常に重要な要素:

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


1
これは私にとってはうまくいきます。おかげで、ええ、私は100%の割合で同意しています。初期状態は{}なので、チェックされた値は未定義になり、制御できなくなります
Ping Woo

13

この問題を解決する簡単な解決策は、デフォルトで空の値を設定することです:

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

8

コンストラクターでフィールド値を ""(空の文字列)に設定することの潜在的な欠点の1つは、フィールドがオプションフィールドであり、編集されないままになっている場合です。フォームを投稿する前にマッサージを行わない限り、フィールドはNULLではなく空の文字列としてデータストレージに保持されます。

この代替方法では、空の文字列を回避します。

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

... 

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

6

onChange={this.onFieldChange('name').bind(this)}入力で使用するときは、プロパティフィールドの値として空の文字列を宣言する必要があります。

間違った方法:

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

正しい方法:

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

6

私の場合、私は本当に些細なことを逃していました。

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

警告を受けたときの私の状態は次のとおりです。

state = {
   myObject: undefined
}

私の値の入力参照するように状態を変更することにより、私の問題は解決されました:

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

2
私が抱えていた本当の問題を理解するのを手伝ってくれてありがとう
rotimi-best

3

コンポーネントの小道具が状態として渡された場合、入力タグのデフォルト値を入力します

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


3

これの更新。React Hooksの場合const [name, setName] = useState(" ")


4更新ジョーダンのおかげで、このエラーを解決するのは少し難しい
Juan Salvador

なぜ" "ありませんか""?これにより、ヒントテキストが失われ、クリックして入力すると、入力するデータの前に卑劣なスペースが表示されます。
ジョシュアウェイド

1

これは通常、アプリケーションの起動時にフィールドの値を制御しておらず、イベントまたは関数が発生した後、または状態が変化した後、入力フィールドの値を制御しようとしている場合にのみ発生します。

入力を制御せずに入力を制御するこの移行が、最初に問題を発生させます。

これを回避する最良の方法は、コンポーネントのコンストラクターで入力の値を宣言することです。したがって、入力要素にはアプリケーションの開始時からの値があります。


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

0

this.state.nameがnullの場合、単に ''へのフォールバックを作成します。

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

これは、useState変数でも機能します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.