Mobile Step 4: State Management
Manage loading states, error states, and message updates properly.
What You'll Learn
- Multiple state variables for different UI states
- Optimistic UI updates
- Loading indicators
- Error display
Step 1: Add State Variables
const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
Three types of state:
- Data: messages — the chat content
- UI: loading — whether a request is in progress
- Error: error — user-visible error message
Step 2: The Send Flow
1. User types message and taps Send
2. Immediately clear input and show loading
3. Optimistically add message to the list
4. Send request to backend
5a. Success → update message with server response + add AI reply
5b. Failure → mark message as failed, show error
6. Hide loading
Step 3: Disable Send While Loading
<TouchableOpacity
style={[styles.sendButton, loading && styles.sendButtonDisabled]}
onPress={handleSend}
disabled={loading}
>
<Text style={styles.sendButtonText}>Send</Text>
</TouchableOpacity>
Step 4: Show an Error Banner
{error && (
<View style={styles.errorBanner}>
<Text style={styles.errorText}>Error: {error}</Text>
<TouchableOpacity onPress={() => setError(null)}>
<Text style={styles.errorDismiss}>X</Text>
</TouchableOpacity>
</View>
)}
Styles:
errorBanner: {
backgroundColor: "#FFE0E0",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
padding: 10,
},
errorText: { color: "#D00", flex: 1 },
errorDismiss: { color: "#D00", fontWeight: "bold", marginLeft: 10, fontSize: 18 },
Step 5: Add a Header
<View style={styles.header}>
<Text style={styles.headerTitle}>Agentic Chat</Text>
</View>
Styles:
header: {
backgroundColor: "#007AFF",
paddingTop: 50,
paddingBottom: 15,
paddingHorizontal: 16,
},
headerTitle: { color: "#FFF", fontSize: 20, fontWeight: "bold" },
Step 6: Handle Keyboard
import { KeyboardAvoidingView, Platform } from "react-native";
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === "ios" ? "padding" : "height"}
>
{/* everything */}
</KeyboardAvoidingView>
This pushes the input row up when the keyboard opens instead of covering it.
Step 7: Understanding useState Updates
// ❌ Wrong — doesn't work reliably with FlatList
messages.push(newMessage);
setMessages(messages);
// ✅ Correct — creates a new array
setMessages([...messages, newMessage]);
// ✅ Correct — uses the previous state
setMessages((prev) => [...prev, newMessage]);
Always create a new array/object instead of mutating the existing one. React detects changes by reference — it needs a new reference to know to re-render.
Step 8: Test the Complete Flow
- Send a message → input clears, loading shows
- Wait for response → message + AI reply appear
- Cause a network error (stop the backend) → error banner shows, message marked as failed
- Dismiss error → banner disappears