الخطوة 4 في تطبيق الجوال: إدارة الحالة

أدر حالات التحميل والأخطاء وتحديثات الرسائل على نحو سليم.

ما ستتعلمه

  • عدة متغيرات حالة لتمثيل حالات مختلفة في واجهة المستخدم
  • تحديثات تفاؤلية في واجهة المستخدم
  • مؤشرات التحميل
  • عرض رسائل الخطأ

الخطوة 1: إضافة متغيرات الحالة

const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState("");
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);

ثلاثة أنواع من الحالة: - البيانات: messages — محتوى الدردشة - واجهة المستخدم: loading — ما إذا كان هناك طلب قيد التنفيذ - الخطأ: error — رسالة خطأ يراها المستخدم

الخطوة 2: مسار الإرسال

1. يكتب المستخدم الرسالة ويضغط إرسال
2. إفراغ حقل الإدخال فورًا وإظهار مؤشر التحميل
3. إضافة الرسالة إلى القائمة تفاؤليًا
4. إرسال الطلب إلى الواجهة الخلفية
5a. النجاح → تحديث الرسالة باستجابة الخادم + إضافة رد الذكاء الاصطناعي
5b. الفشل → تعليم الرسالة بأنها فشلت، وإظهار الخطأ
6. إخفاء مؤشر التحميل

الخطوة 3: تعطيل الإرسال أثناء التحميل

<TouchableOpacity
  style={[styles.sendButton, loading && styles.sendButtonDisabled]}
  onPress={handleSend}
  disabled={loading}
>
  <Text style={styles.sendButtonText}>Send</Text>
</TouchableOpacity>

الخطوة 4: عرض شريط الخطأ

{error && (
  <View style={styles.errorBanner}>
    <Text style={styles.errorText}>Error: {error}</Text>
    <TouchableOpacity onPress={() => setError(null)}>
      <Text style={styles.errorDismiss}>X</Text>
    </TouchableOpacity>
  </View>
)}

الأنماط:

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 },

الخطوة 5: إضافة ترويسة

<View style={styles.header}>
  <Text style={styles.headerTitle}>Agentic Chat</Text>
</View>

الأنماط:

header: {
  backgroundColor: "#007AFF",
  paddingTop: 50,
  paddingBottom: 15,
  paddingHorizontal: 16,
},
headerTitle: { color: "#FFF", fontSize: 20, fontWeight: "bold" },

الخطوة 6: التعامل مع لوحة المفاتيح

import { KeyboardAvoidingView, Platform } from "react-native";

<KeyboardAvoidingView
  style={styles.container}
  behavior={Platform.OS === "ios" ? "padding" : "height"}
>
  {/* everything */}
</KeyboardAvoidingView>

هذا يدفع صف الإدخال إلى الأعلى عند فتح لوحة المفاتيح بدلًا من أن يغطيه.

الخطوة 7: فهم تحديثات useState

// ❌ خطأ — لا يعمل بموثوقية مع FlatList
messages.push(newMessage);
setMessages(messages);

// ✅ صحيح — ينشئ مصفوفة جديدة
setMessages([...messages, newMessage]);

// ✅ صحيح — يستخدم الحالة السابقة
setMessages((prev) => [...prev, newMessage]);

أنشئ دائمًا مصفوفة أو كائنًا جديدًا بدلًا من تعديل الموجود. تكتشف React التغييرات عبر المرجع، فتحتاج إلى مرجع جديد لتعرف أن عليها إعادة العرض.

الخطوة 8: اختبار المسار الكامل

  1. إرسال رسالة → يُفرَّغ حقل الإدخال ويظهر مؤشر التحميل
  2. انتظار الاستجابة → تظهر الرسالة ورد الذكاء الاصطناعي
  3. إحداث خطأ في الشبكة (أوقف الواجهة الخلفية) → يظهر شريط الخطأ وتُعلَّم الرسالة بأنها فشلت
  4. إغلاق الخطأ → يختفي الشريط