React で controlled / uncontrolled input 警告が消せない
「A component is changing an uncontrolled input to be controlled」警告は、value プロップが undefined と確定値の間で切り替わると出る。
初期値を必ず空文字や null 以外で与え、value / defaultValue を混在させないのが原則。
公開: 更新:
要約
React の <input> は 制御 (controlled) と 非制御 (uncontrolled) のどちらかで一貫している必要がある。
途中で value が undefined ↔ 文字列の間を行き来すると、「changing an uncontrolled input to be controlled」警告が出る。
公式の <input> リファレンス(新しいタブで開く) のとおり、最初から非 undefined の値で渡せば解決する。
実行例
実際に上記の 4 つの原因を node:20 で描画してみると、初期値が undefined の input と checked が undefined の checkbox では同じ警告が出るのに対し、value と defaultValue を併用した input だけは別の文面の警告になる。
解決策どおり初期値と ?? フォールバック、Boolean() 化を入れた最後の実行では、警告は一行も出ていない。
$ node bad_initial.js
A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://react.dev/link/controlled-components
<input type=text> value="alice" checked=false$ node bad_checkbox.js
A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://react.dev/link/controlled-components
<input type=checkbox> value="on" checked=true$ node bad_both.js
Form contains an input of type undefined with both value and defaultValue props. Input elements must be either controlled or uncontrolled (specify either the value prop, or the defaultValue prop, but not both). Decide between using a controlled or uncontrolled input element and remove one of these props. More info: https://react.dev/link/controlled-components
<input type=text> value="" checked=false$ node fixed.js
<input type=text> value="alice" checked=false
<input type=text> value="alice" checked=false
<input type=checkbox> value="on" checked=true
$ echo $?
0— 2026-09-16 時点の出力
検証環境
- 検証日
- 実行環境
node:20Debian GNU/Linux 12 (bookworm)- バージョン
- Node.js 20.20.2
- npm 10.8.2
- Python 3.11.2
- Git 2.39.5
この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。
よくある原因
- 初期 state が undefined:
useState()を引数なしで呼ぶと初期値はundefined。
最初のレンダーでvalue={undefined}→ 入力後に文字列、と切り替わる - 非同期データの流し込み:
useEffectでsetValue(data.name)する場合、初回レンダー時はdataが無くundefined - value と defaultValue の併用: 同じ input に両方渡すと React がどちらに従うか決められない
- checked が undefined: checkbox / radio で
checkedを Boolean 化せずに渡している
解決策
1. 初期値を必ず与える
// NG
const [name, setName] = useState();
// OK
const [name, setName] = useState("");input は文字列、checkbox は Boolean、select は文字列 / 数値で空でも明示する。
2. 非同期値はフォールバック
const { data } = useQuery(...);
return (
<input
value={data?.name ?? ""}
onChange={(e) => mutate(e.target.value)}
/>
);?? で undefined のときだけ空文字に落とす。|| だと 0 のような falsy 値も落ちるので注意。
3. 制御 / 非制御を統一
// 制御コンポーネント
<input value={value} onChange={(e) => setValue(e.target.value)} />
// 非制御コンポーネント
<input defaultValue="初期値" ref={inputRef} />両方混ぜない。
フォーム全体を useState で扱うなら defaultValue は使わず value で統一する。
4. checkbox の Boolean 化
<input
type="checkbox"
checked={Boolean(item.done)}
onChange={(e) => toggle(e.target.checked)}
/>item.done が undefined でも Boolean(undefined) は false で安定する。
同じ思想で radio の checked も Boolean に変換する。