TextInput と useState で入力を制御し、「ラベル → 入力欄 → state」の読み方を練習する第4話です。
フォーム入力とバリデーション - 入力値を state で追う
第2話 では、ボタンを押すたびに変わる数字を useState で追いました。今回は キーボード入力 が state に入るフォームを扱います。
登録画面で名前を打ち込むとき、「この入力欄の値はコードのどこで持っているのか」と感じたことはないでしょうか。読み方の軸は第2話と同じです。画面上の値 → {変数} → useState。第4話では TextInput を使った制御コンポーネントと、簡単な必須チェックまでを一通り動かします。
この記事で扱うこと
| 章 | 実装(A) | 読み解き(B) |
|---|---|---|
| 制御コンポーネント | TextInput + value / onChangeText | ラベル文言 → Input → state 変数 |
| 送信 | Pressable + 簡単な必須チェック | エラーメッセージ → バリデーション条件 |
| 第2・3話との接続 | 同じ state の読み方 | Expo Router 画面内でも同型 |
| まとめ | 次に進む順番 | 第5話への導線 |
前提: 第1話、第2話。第3話の Expo Router は任意です(app/signup.tsx のような画面ファイルでも読み方は同じです)。
1. TextInput の最小例 — 制御コンポーネント
第2話のカウンターと同様、ここでは 1 画面・1 ファイル に絞ります。エントリファイル(App.tsx や app/index.tsx など)を次の内容に差し替えてください。
import { useState } from "react";
import {
Pressable,
StyleSheet,
Text,
TextInput,
View,
} from "react-native";
export default function SignUpScreen() {
const [name, setName] = useState("");
return (
<View style={styles.container}>
<Text style={styles.label}>お名前</Text>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
placeholder="山田 太郎"
/>
<Text style={styles.preview}>入力中: {name}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 24,
justifyContent: "center",
gap: 12,
},
label: {
fontSize: 14,
fontWeight: "600",
},
input: {
borderWidth: 1,
borderColor: "#cbd5e1",
borderRadius: 8,
padding: 12,
fontSize: 16,
},
preview: {
color: "#64748b",
},
});
bunx expo start で起動し、入力欄に文字を打つと「入力中: …」がリアルタイムで変われば成功です。
ポイントは次の 3 つです。
TextInputにvalueとonChangeTextをセットで 渡す(制御コンポーネント)placeholderは未入力時のヒント。実際の値はvalue={name}で持つ- 第2話の
count/setCountと同型で、name/setNameが入力用の state になる
2. 読み解きの練習 — ラベルから state まで
第1・2話と同じ型で、いま画面に見えているものから入ります。
- 画面上のラベル「お名前」をエディタで全文検索する
- その直下の
TextInputを確認する value={name}とonChangeText={setName}を辿り、第2話のcount/setCountと対比する- 「入力中: {name}」のプレビューで、キー入力 → state 更新 → 再描画 を言語化する
流れを言葉にすると、こうなります。
キー入力 → onChangeText(setName)→ name が更新 → 再描画 → 入力欄とプレビューが変わる
読み解きのキーワードは次のとおりです。
ラベル文言 → TextInput → value / onChangeText → useState
3. 送信と簡単なバリデーション
送信ボタンとエラー表示を足します。空のまま送ろうとしたときだけ、メッセージを出す最小例です。
import { useState } from "react";
import {
Pressable,
StyleSheet,
Text,
TextInput,
View,
} from "react-native";
export default function SignUpScreen() {
const [name, setName] = useState("");
const [error, setError] = useState("");
function handleSubmit() {
if (name.trim() === "") {
setError("お名前を入力してください");
return;
}
setError("");
// 第5話で API 送信へ拡張。ここではログで十分
console.log("送信:", name.trim());
}
return (
<View style={styles.container}>
<Text style={styles.label}>お名前</Text>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
placeholder="山田 太郎"
/>
{error ? <Text style={styles.error}>{error}</Text> : null}
<Pressable style={styles.button} onPress={handleSubmit}>
<Text style={styles.buttonLabel}>送信</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 24,
justifyContent: "center",
gap: 12,
},
label: {
fontSize: 14,
fontWeight: "600",
},
input: {
borderWidth: 1,
borderColor: "#cbd5e1",
borderRadius: 8,
padding: 12,
fontSize: 16,
},
error: {
color: "#dc2626",
fontSize: 14,
},
button: {
marginTop: 8,
paddingVertical: 12,
borderRadius: 8,
backgroundColor: "#0f172a",
alignItems: "center",
},
buttonLabel: {
color: "#fff",
fontSize: 16,
fontWeight: "600",
},
});
名前を入れずに「送信」を押すと赤いエラーが出ます。名前を入れて再度押すとエラーが消え、コンソールに 送信: … が出ます。
読み解きの練習 — エラー文言から条件まで
- 画面の「お名前を入力してください」をエディタで検索する
{error}またはsetError("お名前を入力してください")を特定するhandleSubmit内のif (name.trim() === "")まで辿る
エラー表示も、第2話の数字と同じく state(error)→ JSX の流れで読めます。
つまずきやすい点
| 症状 | 原因の目安 |
|---|---|
| 入力しても文字が消える / 打てない | value だけあって onChangeText がない |
| エラーが消えない | 成功時に setError("") しているか |
| 送信しても何も起きない | onPress={handleSubmit} が付いているか |
日本語入力(IME)の途中変換や、キーボードで入力欄が隠れる問題は本記事では扱いません。KeyboardAvoidingView などは必要になったときに調べれば十分です。
4. 第2話・第3話との接続
| 話 | 接続 |
|---|---|
| 第2話 | count / setCount → name / setName。更新の仕組みは同じ |
| 第3話 | app/signup.tsx のような画面ファイルでも、ファイル内の読み方は同型 |
メールアドレスなどフィールドが増えたら、state を分けます。
const [name, setName] = useState("");
const [email, setEmail] = useState("");
本記事では 名前 1 つに絞る と、ラベル → Input → state の練習がしやすいです。react-hook-form や Zod などのライブラリはスコープ外です。サーバーへの POST 送信も、第5話で fetch の入口を扱います。
5. まとめ
実装の順番
TextInputにvalueとonChangeTextを渡す- 入力値は
useStateで持つ - 送信前に簡単な必須チェックを入れる
読み解きの順番
- ラベルまたはプレースホルダーから
TextInputを探す value={...}から state 変数を特定する- エラー文言からバリデーション条件を辿る
ここまでできれば、ユーザー入力を扱う画面を読める土台ができています。
- 第1話: React Native 入門
- 第2話: useState で画面を動かす
- 第3話: Expo Router 入門
- 第5話: API とリスト — 取得したデータを画面に並べる