ReactのWarning: A component is changing an uncontrolled input of type text to be controlled.を解決したい

ReactのWarning: A component is changing an uncontrolled input of type text to be controlled.を解決したい JavaScript

Reactでアプリを作っていたらこんなエラーが出ました↓

Warning: A component 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. 

<input>操作時に出るエラーですね。調べても全然情報が出てこない。

一応解決はできましたが理由はよくわからず…でも原因不明のまま放置というわけにもいかないので、わかったことと解決策を共有します。

 

※この記事ももしかしたら間違っている部分があるかもしれないので参考程度に留めておいて下さい。

 

スポンサーリンク

Reactのフォームのcontrolledとuncontrolled

Reactのフォーム関連のコンポーネントには「controlled」と「uncontrolled」という状態があり、

  • controlled:値がステートで管理されている
  • uncontrolled:値がステートで管理されていない

という状態を指します。

<input>の値(value)は大体ステートで管理すると思うので、controlledですね。

また、ステートの値はsetStateしないと変更できません。

 

uncontrolledエラーの原因

そしてこのエラーが起きる原因は「何らかの原因でフォームのコンポーネントがcontrolledからuncontrolledに変わってしまったから」です。React的にはcontrolledのコンポーネントがuncontrolledになったりするのはアウトなんですね(上のエラーにも書いてありますね)

何らかの原因として考えられるうちの1つは、「ステートの値がnullかundefinedになってしまうタイミングがある」です。

 

uncontrolledエラー対処法

というわけで、フォーム関連のコンポーネントのステートにnullやundefinedが設定されていないか確認してみましょう。

ステートの初期値に入れてしまっている人は代わりに空文字('')とかにするといいと思います。

// ダメな例
this.state = {
  value: undefined,
};

// ダメな例
this.state = {};

 

また、setStateでnullやundefinedを入れるのもアウトです。

this.setState({ 
  value: undefined,
}); 

 

と、ここまでは他の記事でも書いてあったのですが、僕の場合は解決しませんでした。というかnullやundefinedになってるステートがないし、コードをいくら見返しても間違ってる箇所がない。多分どこか間違ってるんだろうけど全くわからない。困った。

 

僕の場合のuncontrolledエラー対処法

とはいえ一応解決はできたのでやり方を解説します。参考になるかわかりませんが…

以下エラーが出た僕のコードです(コードそのままではないですが、状況は大体同じものです)。

子コンポーネント(<Form />)

export default class Form extends Component {

  // 他の処理で使われるステートなどがありましたが、関係ないので省略

  render() {
    return (
      <form onSubmit={this.props.onSubmit}>
        <input type="text" value={this.props.text} onChange={this.props.onChange} />
        <input type="submit" value="送信" />
      </form>
    );
  }
}

テキストを入力し、送信するだけの簡単なフォームです。処理はpropsで親コンポーネントに書きます。

 

親コンポーネント(<App />)

export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      value: ''
    }
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(e) {
    e.preventDefault();
    alert(this.state.value)
  }
  handleChange(e) {
    this.setState({ value: e.target.value });
  }

  render() {
    return (
      <div>
        <Form
          text={this.state.value}
          onChange={this.handleChange}
          onSubmit={this.handleSubmit} />
      </div>
    )
  }
}

入力されたテキストをstateに保管し、アラートで出力する簡単な処理です。

正直エラーなんて出るはずがないレベルの処理ですが、上記のエラーでどうしようもない状態…

 

僕が解決した方法は入力された値(value)をHTML本来の動作に任せるというもので、一応これで解決しました。

コードを以下のように直します。

子コンポーネント(<Form />)

export default class Form extends Component {

  // 他の処理で使われるステートなどがありましたが、関係ないので省略

  render() {
    return (
      <form onSubmit={this.props.onSubmit}>
        <input type="text" onChange={this.props.onChange} />{/* ←valueを削除 */}
        <input type="submit" value="送信" />
      </form>
    );
  }
}

<input>valueを削除してHTML本来の動作に任せます。

 

親コンポーネント(<App />)

export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      value: ''
    }
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(e) {
    e.preventDefault();
    alert(this.state.value)
  }
  handleChange(e) {
    this.setState({ value: e.target.value });
  }

  render() {
    return (
      <div>
        {/* text={this.state.value}を削除 */}
        <Form
          onChange={this.handleChange}
          onSubmit={this.handleSubmit} />
      </div>
    )
  }
}

<Form />からtext={this.state.value}を削除し、<input>valueをステートで管理しないようにします。

なお、<input>にはonChange={this.props.onChange}が指定してあるので、onChange時にe.target.value<input>valueを引き続き取得できます。

 

僕の場合はこれで直りました。なぜ直ったかはわかりませんが…。

 

uncontrolledエラー参考記事

解決時に参考にしたリンクを貼ります。

ここで解説したことは間違っているかもしれないので、自分で色々な記事を参考にしながら頑張ってみてください。