React Native Basics: Build Your First Mobile App
State and User Input
In this lesson you will
- Use useState to change what a screen shows when the user taps
- Build a controlled TextInput and read what the user typed
- Disable a button until a form is valid, and keep the keyboard from covering inputs
Introduction
Until now your screens have been still pictures. An app comes alive when it reacts to the user: a tap changes a counter, typing fills a form, a switch turns something on. In React that memory is called state, and it works in React Native exactly as it does on the web. Only the events have different names.
Events: onPress and onChangeText
On the web you handle onClick. A phone has no mouse, so the names differ:
onPresson aPressable(orButton) runs when the user taps.onChangeTexton aTextInputruns every time the text changes, and gives you the new text as a string.onSubmitEditingon aTextInputruns when the user presses the keyboard's return or done key.
A tap counter
React (TSX)
import { useState } from "react";
import { Pressable, StyleSheet, Text, View } from "react-native";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<View style={styles.screen}>
<Text style={styles.count}>{count}</Text>
<Pressable style={styles.button} onPress={() => setCount(count + 1)}>
<Text style={styles.label}>Add one</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, alignItems: "center", justifyContent: "center", gap: 16 },
count: { fontSize: 48, fontWeight: "bold" },
button: { backgroundColor: "#5b4bf5", paddingVertical: 12, paddingHorizontal: 24, borderRadius: 8 },
label: { color: "white", fontWeight: "bold" },
});useState(0) gives you the current value, count, and a function to change it, setCount. When you call setCount, React re-renders the screen with the new value. You never change count directly.
Quick quiz
Which prop runs a function when the user taps a Pressable?
A controlled TextInput
A controlled input keeps its text in state. The input shows whatever the state holds, and every keystroke updates the state. That way your code always knows what was typed.
React (TSX)
import { useState } from "react";
import { StyleSheet, Text, TextInput, View } from "react-native";
export default function NameBox() {
const [name, setName] = useState("");
return (
<View style={styles.screen}>
<TextInput
style={styles.input}
value={name}
onChangeText={setName}
placeholder="Your name"
autoCapitalize="words"
/>
<Text>{name ? `Hello, ${name}!` : "Type your name above"}</Text>
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, justifyContent: "center", padding: 24, gap: 12 },
input: { borderWidth: 1, borderColor: "#ccc", borderRadius: 8, padding: 12, fontSize: 16 },
});onChangeText={setName} works because onChangeText calls its function with the new text, and setName accepts exactly that. The greeting updates as you type.
A form with validation
Real forms have several inputs and a button that should stay disabled until the data is acceptable. Compute that from state instead of storing it separately.
React (TSX)
import { useState } from "react";
import { Alert, Pressable, StyleSheet, Text, TextInput, View } from "react-native";
export default function SignUp() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const valid = email.includes("@") && password.length >= 8;
function submit() {
Alert.alert("Welcome!", `Signed up as ${email}`);
}
return (
<View style={styles.screen}>
<TextInput
style={styles.input}
value={email}
onChangeText={setEmail}
placeholder="Email"
keyboardType="email-address"
autoCapitalize="none"
autoCorrect={false}
/>
<TextInput
style={styles.input}
value={password}
onChangeText={setPassword}
placeholder="Password (8 or more characters)"
secureTextEntry
onSubmitEditing={() => valid && submit()}
/>
<Pressable
style={[styles.button, !valid && styles.disabled]}
disabled={!valid}
onPress={submit}
>
<Text style={styles.label}>Sign up</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, justifyContent: "center", padding: 24, gap: 12 },
input: { borderWidth: 1, borderColor: "#ccc", borderRadius: 8, padding: 12, fontSize: 16 },
button: { backgroundColor: "#5b4bf5", padding: 14, borderRadius: 8, alignItems: "center" },
disabled: { opacity: 0.4 },
label: { color: "white", fontWeight: "bold" },
});What is new here:
validis not state. It is worked out fromemailandpasswordeach time the screen draws, so it can never be out of date.secureTextEntryhides the characters of a password.disabledstops the taps from doing anything, and thedisabledstyle dims the button so the user can see it.Alert.alertshows the phone's own pop-up dialog, which looks right on both iPhone and Android.
Quick quiz
Why is valid worked out from email and password instead of stored with useState?
Keeping the keyboard out of the way
On a phone the keyboard slides up and can cover your inputs. KeyboardAvoidingView moves its content out of the way. The right behavior differs by platform:
React (TSX)
import { KeyboardAvoidingView, Platform, StyleSheet, TextInput } from "react-native";
export default function ChatBar() {
return (
<KeyboardAvoidingView
style={styles.screen}
behavior={Platform.OS === "ios" ? "padding" : "height"}
>
<TextInput style={styles.input} placeholder="Write a message" />
</KeyboardAvoidingView>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, justifyContent: "flex-end", padding: 16 },
input: { borderWidth: 1, borderColor: "#ccc", borderRadius: 8, padding: 12 },
});A common choice is "padding" on iPhone and "height" on Android. Always test with the real keyboard on both, because the best setting depends on your layout.
Try it yourself
Build a "tip calculator": one TextInput for the bill (with keyboardType="numeric"), a Text showing the bill plus 10 percent, and a Pressable that clears the input. Calculate the tip from the state instead of storing it.
Summary
- State with
useStateworks as in React for the web. The events areonPressfor taps andonChangeTextfor typing. - A controlled
TextInputtakes itsvaluefrom state and updates it inonChangeText. - Calculate things like "is the form valid" from state, use
Alert.alertfor pop-ups, andKeyboardAvoidingViewto keep inputs visible.
Lesson complete
+20 XP
Nice work. Keep going while it is fresh.