📱 User Interaction: Forms and UI States in React Native
🎭 The Magic Mailbox Story
Imagine you have a magic mailbox at home. This mailbox is special because:
- It remembers everything you put inside it
- It checks if your letters are written correctly
- It tells you when something is wrong
- It shows you when it’s busy sending your mail
- It lets you know when there’s nothing inside
React Native forms work exactly like this magic mailbox! Let’s explore how.
📝 Form Handling
What is Form Handling?
Think of form handling like a teacher collecting homework papers. The teacher needs to:
- Collect each paper (get the input)
- Keep track of who gave what (store the data)
- Do something with all the papers (submit the form)
Simple Example:
import { useState } from 'react';
import {
View, TextInput, Button, Text
} from 'react-native';
function SimpleForm() {
const [name, setName] = useState('');
const handleSubmit = () => {
console.log('Hello, ' + name);
};
return (
<View>
<TextInput
value={name}
onChangeText={setName}
placeholder="Your name"
/>
<Button
title="Say Hello"
onPress={handleSubmit}
/>
</View>
);
}
What’s happening:
useState('')→ Creates an empty box to store the nameonChangeText={setName}→ Updates the box when you typehandleSubmit→ Uses the stored name when you press the button
🎮 Controlled Components
What are Controlled Components?
Imagine a puppet on strings. You (the puppeteer) control every movement. The puppet doesn’t move on its own – it only moves when you tell it to.
Controlled components are inputs that React fully controls:
- React holds the value
- React updates the value
- The input shows whatever React tells it to show
function ControlledInput() {
const [email, setEmail] = useState('');
return (
<TextInput
value={email} // React says what to show
onChangeText={setEmail} // React updates when typed
placeholder="Enter email"
keyboardType="email-address"
/>
);
}
Why Use Controlled Components?
graph TD A["User Types"] --> B["onChangeText fires"] B --> C["setEmail updates state"] C --> D["Component re-renders"] D --> E["TextInput shows new value"] E --> A
Benefits:
- ✅ You always know what’s in the input
- ✅ You can validate as the user types
- ✅ You can transform input (like making text uppercase)
- ✅ You have one source of truth
✅ Form Validation
What is Form Validation?
Remember our magic mailbox? Before sending a letter, it checks:
- Is there a stamp? ✉️
- Is the address written? 📍
- Is everything spelled correctly? ✏️
That’s validation! Making sure the information is correct before using it.
Simple Validation Example:
function LoginForm() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [errors, setErrors] = useState({});
const validate = () => {
const newErrors = {};
// Check email
if (!email.includes('@')) {
newErrors.email = 'Need a real email!';
}
// Check password
if (password.length < 6) {
newErrors.password = 'Too short!';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = () => {
if (validate()) {
console.log('Form is good!');
}
};
return (
<View>
<TextInput
value={email}
onChangeText={setEmail}
placeholder="Email"
/>
<TextInput
value={password}
onChangeText={setPassword}
placeholder="Password"
secureTextEntry
/>
<Button
title="Login"
onPress={handleSubmit}
/>
</View>
);
}
Common Validation Rules
| What to Check | How to Check |
|---|---|
| Not empty | value.length > 0 |
| Valid email | value.includes('@') |
| Min length | value.length >= 6 |
| Numbers only | /^\d+$/.test(value) |
| Match fields | password === confirmPassword |
🚨 Error State Display
Showing Errors Nicely
When something’s wrong, we need to tell the user clearly – like a friend pointing out spinach in your teeth! 🥬
function FormWithErrors() {
const [email, setEmail] = useState('');
const [error, setError] = useState('');
const checkEmail = (text) => {
setEmail(text);
if (text && !text.includes('@')) {
setError('Please enter a valid email');
} else {
setError('');
}
};
return (
<View>
<TextInput
value={email}
onChangeText={checkEmail}
placeholder="Email"
style={{
borderColor: error ? 'red' : 'gray',
borderWidth: 1,
padding: 10,
}}
/>
{error ? (
<Text style={{ color: 'red' }}>
⚠️ {error}
</Text>
) : null}
</View>
);
}
Error Display Flow
graph TD A["User Types"] --> B{Is Input Valid?} B -->|Yes| C["Clear Error"] B -->|No| D["Set Error Message"] C --> E["Normal Border"] D --> F["Red Border + Error Text"]
Best Practices:
- 🔴 Use red color for errors
- 📍 Show error near the problem field
- 💬 Be helpful, not scary
- ⏰ Show errors at the right time (not too early!)
⏳ Loading State UI
What is Loading State?
When you ask your phone to do something, sometimes it takes a moment. Like waiting for pizza to be delivered! 🍕
During this wait, we show a loading state so users know something is happening.
import { ActivityIndicator } from 'react-native';
function SubmitButton() {
const [loading, setLoading] = useState(false);
const handlePress = async () => {
setLoading(true);
// Pretend to send data (wait 2 seconds)
await new Promise(r => setTimeout(r, 2000));
setLoading(false);
};
return (
<TouchableOpacity
onPress={handlePress}
disabled={loading}
style={{
backgroundColor: loading ? '#ccc' : 'blue',
padding: 15,
borderRadius: 8,
}}
>
{loading ? (
<ActivityIndicator color="white" />
) : (
<Text style={{ color: 'white' }}>
Submit
</Text>
)}
</TouchableOpacity>
);
}
Loading State Rules
| Do ✅ | Don’t ❌ |
|---|---|
| Show spinner | Leave blank screen |
| Disable buttons | Let users tap again |
| Gray out form | Pretend nothing’s happening |
| Keep user informed | Make them guess |
📭 Empty State UI
What is Empty State?
Imagine opening a toy box and finding it empty. Instead of just showing emptiness, you could put a note: “Your toys are waiting to be found! Go play outside! 🎈”
That’s empty state UI – making “nothing here” feel friendly and helpful.
function MessageList() {
const [messages, setMessages] = useState([]);
if (messages.length === 0) {
return (
<View style={styles.emptyContainer}>
<Text style={styles.emoji}>📭</Text>
<Text style={styles.title}>
No Messages Yet
</Text>
<Text style={styles.subtitle}>
When you receive messages,
they'll appear here!
</Text>
<Button
title="Invite Friends"
onPress={() => {}}
/>
</View>
);
}
return (
<FlatList
data={messages}
renderItem={({ item }) => (
<Text>{item.text}</Text>
)}
/>
);
}
Great Empty States Include
graph TD A["Empty State"] --> B["Friendly Icon/Image"] A --> C["Clear Message"] A --> D["Helpful Action"] B --> E["📭 🎨 🌟"] C --> F["No items yet"] D --> G["Button to Add/Create"]
🎁 Putting It All Together
Here’s a complete form with ALL the states:
function CompleteForm() {
const [name, setName] = useState('');
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const [submitted, setSubmitted] = useState(false);
const validate = () => {
if (name.length < 2) {
setError('Name is too short');
return false;
}
setError('');
return true;
};
const handleSubmit = async () => {
if (!validate()) return;
setLoading(true);
await new Promise(r => setTimeout(r, 1500));
setLoading(false);
setSubmitted(true);
};
// Success state (after submit)
if (submitted) {
return (
<View style={styles.success}>
<Text>✅ Thank you, {name}!</Text>
</View>
);
}
return (
<View>
{/* Input with error state */}
<TextInput
value={name}
onChangeText={setName}
placeholder="Your name"
style={{
borderColor: error ? 'red' : '#ddd',
borderWidth: 1,
padding: 12,
}}
/>
{/* Error display */}
{error ? (
<Text style={{ color: 'red' }}>
{error}
</Text>
) : null}
{/* Button with loading state */}
<TouchableOpacity
onPress={handleSubmit}
disabled={loading}
>
{loading ? (
<ActivityIndicator />
) : (
<Text>Submit</Text>
)}
</TouchableOpacity>
</View>
);
}
🌟 Quick Summary
| Concept | What It Does | Like… |
|---|---|---|
| Form Handling | Collects & manages input | Teacher collecting papers |
| Controlled Components | React controls input value | Puppet on strings |
| Form Validation | Checks if data is correct | Spell checker |
| Error State | Shows what’s wrong | Helpful friend |
| Loading State | Shows “please wait” | Pizza delivery tracker |
| Empty State | Friendly “nothing here” | Decorated empty box |
🚀 You Did It!
Now you understand how React Native forms work:
- Collect information with controlled components
- Validate to make sure it’s correct
- Show errors when something’s wrong
- Display loading while waiting
- Handle empty states gracefully
Forms are like magic mailboxes – they collect, check, wait, and communicate. And now you know how to build them! 🎉
