ReactでonChangeが動かない|入力フォームの基本
ReactでonChangeが動かない原因と解決方法を詳しく解説。入力フォームの基本的な実装から応用まで、実際のコード例で説明します。
みなさん、ReactでonChangeが動かなくて困ったことはありませんか?
「入力しても画面が更新されない」「値が取得できない」と悩んだ経験はありませんか?
この記事では、ReactでonChangeが動かない原因と解決方法を詳しく解説します。 難しそうに見えますが、実は基本を押さえれば簡単に解決できるんです。
一緒にonChangeの仕組みをマスターして、スムーズに動くフォームを作りましょう!
onChangeが動かない主な原因
stateの更新方法が間違っている
最も多い原因は、stateの更新方法が間違っていることです。
import React, { useState } from 'react';
// ❌ 間違った例
function BrokenForm() {
const [inputValue, setInputValue] = useState('');
const handleChange = (e) => {
// 間違い:stateを直接変更しようとしている
inputValue = e.target.value; // これは動かない
};
return (
<div>
<input
type="text"
value={inputValue}
onChange={handleChange}
/>
<p>入力値: {inputValue}</p>
</div>
);
}
上のコードでは、inputValue = e.target.value
でstateを直接変更しようとしています。
これではReactが変更を検知できないので、画面が更新されません。
// ✅ 正しい例
function CorrectForm() {
const [inputValue, setInputValue] = useState('');
const handleChange = (e) => {
// 正しい:setState関数を使用
setInputValue(e.target.value);
};
return (
<div>
<input
type="text"
value={inputValue}
onChange={handleChange}
/>
<p>入力値: {inputValue}</p>
</div>
);
}
setInputValue
を使うことで、Reactが変更を検知して画面を更新します。
イベントハンドラーの書き方が間違っている
onChangeの書き方にも注意が必要です。
// ❌ 間違ったイベントハンドラーの書き方
function WrongEventHandler() {
const [value, setValue] = useState('');
return (
<input
type="text"
value={value}
// 間違い:関数を即座に実行している
onChange={setValue(e.target.value)}
/>
);
}
onChange={setValue(e.target.value)}
と書くと、関数が即座に実行されてしまいます。
// ✅ 正しいイベントハンドラーの書き方
function CorrectEventHandler() {
const [value, setValue] = useState('');
return (
<input
type="text"
value={value}
// 正しい:関数を渡している
onChange={(e) => setValue(e.target.value)}
/>
);
}
onChange={(e) => setValue(e.target.value)}
のように、アロー関数で関数を渡します。
イベントオブジェクトを受け取っていない
イベントハンドラーでイベントオブジェクトを受け取り忘れることも多いです。
// ❌ 間違い:イベントオブジェクトを受け取っていない
function EventError() {
const [value, setValue] = useState('');
const handleChange = () => {
// 間違い:イベントオブジェクトがない
setValue(value); // 古い値を設定してしまう
};
return (
<input
type="text"
value={value}
onChange={handleChange}
/>
);
}
イベントオブジェクト(e
)を受け取らないと、入力された値を取得できません。
// ✅ 正しい:イベントオブジェクトを受け取る
function EventCorrect() {
const [value, setValue] = useState('');
const handleChange = (e) => {
// 正しい:イベントオブジェクトから値を取得
setValue(e.target.value);
};
return (
<input
type="text"
value={value}
onChange={handleChange}
/>
);
}
handleChange = (e) => {}
のように、必ずイベントオブジェクトを受け取りましょう。
制御されたコンポーネントの基本
Reactでフォームを扱う際は、制御されたコンポーネントの概念を理解することが重要です。
制御されたコンポーネントとは
制御されたコンポーネントとは、Reactのstateによって値が制御されるコンポーネントのことです。
function ControlledInput() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
return (
<form>
<div>
<label>名前:</label>
<input
type="text"
value={name} // stateで値を制御
onChange={(e) => setName(e.target.value)}
/>
</div>
<div>
<label>メール:</label>
<input
type="email"
value={email} // stateで値を制御
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<p>名前: {name}</p>
<p>メール: {email}</p>
</form>
);
}
value
プロパティにstateを指定することで、Reactがinputの値を管理します。
onChange
でstateを更新することで、入力に応じて画面が更新されます。
制御されていないコンポーネント
制御されていないコンポーネントでは、DOMが値を管理します。
function UncontrolledInput() {
const inputRef = useRef(null);
const handleSubmit = () => {
// DOMから直接値を取得
console.log(inputRef.current.value);
};
return (
<div>
<input
type="text"
ref={inputRef}
// valueプロパティを設定しない
/>
<button onClick={handleSubmit}>送信</button>
</div>
);
}
制御されていないコンポーネントでは、ref
を使ってDOMから直接値を取得します。
複数の入力フィールドを効率的に管理
複数の入力フィールドがある場合は、オブジェクトでstateを管理すると便利です。
非効率な方法
// ❌ 非効率な方法:各フィールドに個別のstate
function MultipleFieldsBad() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [phone, setPhone] = useState('');
return (
<form>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
type="tel"
value={phone}
onChange={(e) => setPhone(e.target.value)}
/>
</form>
);
}
フィールドが多くなると、stateとイベントハンドラーが大量に必要になります。
効率的な方法
// ✅ 効率的な方法:オブジェクトでstateを管理
function MultipleFieldsGood() {
const [formData, setFormData] = useState({
name: '',
email: '',
phone: ''
});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
return (
<form>
<input
type="text"
name="name"
value={formData.name}
onChange={handleChange}
/>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
/>
<input
type="tel"
name="phone"
value={formData.phone}
onChange={handleChange}
/>
</form>
);
}
1つのオブジェクトでstateを管理し、1つのイベントハンドラーで全てのフィールドに対応します。
const { name, value } = e.target
で入力フィールドの名前と値を取得。
[name]: value
でオブジェクトの該当プロパティを更新します。
実践的なフォームを作ってみよう
バリデーション付きログインフォーム
function LoginForm() {
const [credentials, setCredentials] = useState({
username: '',
password: ''
});
const [errors, setErrors] = useState({});
const handleChange = (e) => {
const { name, value } = e.target;
setCredentials(prev => ({
...prev,
[name]: value
}));
// エラーをクリア
if (errors[name]) {
setErrors(prev => ({
...prev,
[name]: ''
}));
}
};
const handleSubmit = (e) => {
e.preventDefault();
// バリデーション
const newErrors = {};
if (!credentials.username) {
newErrors.username = 'ユーザー名を入力してください';
}
if (!credentials.password) {
newErrors.password = 'パスワードを入力してください';
}
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
// ログイン処理
console.log('ログイン情報:', credentials);
};
return (
<form onSubmit={handleSubmit}>
<div>
<label>ユーザー名:</label>
<input
type="text"
name="username"
value={credentials.username}
onChange={handleChange}
/>
{errors.username && <span className="error">{errors.username}</span>}
</div>
<div>
<label>パスワード:</label>
<input
type="password"
name="password"
value={credentials.password}
onChange={handleChange}
/>
{errors.password && <span className="error">{errors.password}</span>}
</div>
<button type="submit">ログイン</button>
</form>
);
}
このフォームでは、入力値の管理とバリデーションを同時に行っています。
handleChange
で入力値を更新し、同時にエラーをクリアします。
handleSubmit
でバリデーションを実行し、エラーがあれば表示します。
動的なフォーム項目
function DynamicForm() {
const [items, setItems] = useState([{ id: 1, name: '', price: '' }]);
const handleChange = (id, field, value) => {
setItems(prev => prev.map(item =>
item.id === id ? { ...item, [field]: value } : item
));
};
const addItem = () => {
const newId = Math.max(...items.map(item => item.id)) + 1;
setItems(prev => [...prev, { id: newId, name: '', price: '' }]);
};
const removeItem = (id) => {
setItems(prev => prev.filter(item => item.id !== id));
};
return (
<div>
<h3>商品リスト</h3>
{items.map(item => (
<div key={item.id} className="item-row">
<input
type="text"
placeholder="商品名"
value={item.name}
onChange={(e) => handleChange(item.id, 'name', e.target.value)}
/>
<input
type="number"
placeholder="価格"
value={item.price}
onChange={(e) => handleChange(item.id, 'price', e.target.value)}
/>
<button onClick={() => removeItem(item.id)}>削除</button>
</div>
))}
<button onClick={addItem}>項目を追加</button>
</div>
);
}
handleChange
で特定のアイテムの特定のフィールドを更新します。
addItem
で新しい項目を追加し、removeItem
で項目を削除します。
デバッグのコツ
onChangeが動かない場合のデバッグ方法をご紹介します。
console.logを使った確認
function DebugForm() {
const [value, setValue] = useState('');
const handleChange = (e) => {
console.log('onChange発火:', e.target.value); // デバッグ用
setValue(e.target.value);
};
console.log('現在のstate:', value); // デバッグ用
return (
<input
type="text"
value={value}
onChange={handleChange}
/>
);
}
console.log
でイベントの発火と値の変化を確認できます。
カスタムフックで再利用
// カスタムフック
function useForm(initialValues) {
const [values, setValues] = useState(initialValues);
const handleChange = useCallback((e) => {
const { name, value } = e.target;
setValues(prev => ({
...prev,
[name]: value
}));
}, []);
const reset = useCallback(() => {
setValues(initialValues);
}, [initialValues]);
return { values, handleChange, reset };
}
// 使用例
function FormWithCustomHook() {
const { values, handleChange, reset } = useForm({
name: '',
email: ''
});
return (
<form>
<input
name="name"
value={values.name}
onChange={handleChange}
/>
<input
name="email"
value={values.email}
onChange={handleChange}
/>
<button type="button" onClick={reset}>リセット</button>
</form>
);
}
カスタムフックを使うことで、フォームのロジックを再利用できます。
まとめ
ReactでonChangeが動かない問題について、原因と解決方法を詳しく解説しました。
主な原因と解決方法をおさらいしましょう:
- stateの更新:必ずsetState関数を使用する
- イベントハンドラー:関数を実行せず、関数を渡す
- イベントオブジェクト:必ず受け取って値を取得する
効率的なフォーム管理のポイントです:
- 複数フィールドはオブジェクトで管理
- カスタムフックで再利用可能にする
- バリデーションも組み込む
デバッグの手順も覚えておきましょう:
- console.logで値の変化を確認
- React Developer Toolsでstateを監視
- イベントハンドラーの動作を確認
これらのポイントを押さえることで、onChangeが動かない問題を解決できます。 制御されたコンポーネントの概念を理解して、安定したフォームを作成しましょう。
ぜひ実際のプロジェクトで試してみてください!