フックuseState()
でテキスト入力フィールド(<input type="text">
)をコントロールしようとしたら、ブラウザコンソールに何やら警告が出てしまいました。
制御されたコンポーネントを使うよう促す警告
具体的にはつぎのメッセージで、制御されていない<input type="text">
要素を制御されたコンポーネントに変換しようとしていると警告しています。コントロールしたいのなら、制御されたコンポーネントを使わなければならないということのようです。
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.
警告(Warning)メッセージから検索してこの記事にたどり着き、とにかく結論が知りたいという方は、フックuseState()
に初期値を与えてください。
// const [text, setText] = useState();
const [text, setText] = useState(''); // 初期値を与える
では改めて、経緯からお話ししましょう。
警告を受けた関数コンポーネントのコード
警告を受けた関数コンポーネントのコードは、きわめて単純化するとつぎのとおりです。これで前掲の警告が示されます。CodeSandbox「React: Warning - Changing uncontrolled input with hook」にサンプルを掲げましたので、実際の動きはこちらでお確かめください。フィールドにテキストを入力した最初に、ブラウザコンソールにメッセージが出力されます。警告ですので、動作しないわけではありません。
function App() {
const [text, setText] = useState();
const handleChange = (event) => {
const target = event.target;
setText(target.value);
};
return (
<div>
<input type="text"
value={text}
onChange={handleChange}
/>
</div>
);
}
クラスコンポーネントでは警告されない
警告メッセージはさらに、詳しくはこちらを見るようにと「制御されたコンポーネント」の説明がリンクされています。ただ、例示されているコードはクラスコンポーネントです。たしかに、つぎのようなクラスコンポーネントに書き替えれば警告は出ません。
class App extends React.Component {
constructor() {
super();
this.state = {};
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const target = event.target;
console.log('change:', target.value);
};
render() {
return (
<div>
<input type="text"
value={this.state.value}
onChange={this.handleChange}
/>
</div>
);
}
}
useState()フックには初期値を与える
けれど、このクラスコンポーネントをフックで関数に改めたのが、冒頭のコンポーネントの定めです。処理の組み立てに違いがあるようには見えません。
ようやく判明したのが、フックuseState()
に初期値を与えれば警告が消えるということです。クラスコンポーネントの例でstate
の初期値に設定したのは空のオブジェクト{}
ですから、ちょっと納得はいきません。少なくとも、警告メッセージにはもう少し情報がほしいところです。
// const [text, setText] = useState();
const [text, setText] = useState(''); // 初期値を与える
公開したCodeSandbox「React: Warning - Changing uncontrolled input with hook」は、src/index.js
がルートモジュールのsrc/App.js
を読み込んでいます。クラスコンポーネントsrc/App_class.js
と関数コンポーネントsrc/App_hook.js
のモジュールも別途加えました。それぞれをsrc/App.js
に差し替えて、結果の違いをお確かめください。