الدرس 11 · الوحدة 5: تطوير تطبيقات باستخدام React Native
جلب البيانات وعرضها في قائمة حيّة
إحضار البيانات من الشبكة وعرضها بأمان. ⏱ نحو 40 دقيقة.
بعد هذا الدرس ستستطيع أن:
- تفهم أن البيانات تأتي لاحقًا بثلاث حالات.
- تجلب البيانات بـ
useEffectوتدير حالة التحميل. - تعالج الخطأ بـ
catchوتمنع التحديث بعد الإزالة.
١البيانات تأتي لاحقًا
تطبيقٌ حقيقيّ يجلب بياناته من خادم عبر الشبكة. والشبكة غير فورية: يمرّ التطبيق بثلاث حالات — تحميل، ثم نجاح (بيانات) أو خطأ. نعرض لكلٍّ واجهةً مناسبة:
| الحالة | ما نعرضه |
|---|---|
| تحميل | مؤشّر «جارٍ التحميل…» |
| نجاح | البيانات في قائمة |
| خطأ | رسالة ونعرض زرّ إعادة |
٢النمط: useEffect + حالة تحميل
نجلب البيانات مرّةً عند ظهور المكوّن (useEffect بمصفوفةٍ فارغة)، ونحفظ حالتَي التحميل والبيانات:
import { useState, useEffect } from 'react';import { View, Text, FlatList, ActivityIndicator, StyleSheet } from 'react-native'; // محاكاة خادم: يردّ بعد نصف ثانيةfunction fetchProducts() { return new Promise((resolve) => { setTimeout(() => resolve([ { id: '1', name: 'قهوة', price: 20 }, { id: '2', name: 'شاي', price: 12 }, { id: '3', name: 'عصير', price: 15 }, ]), 500); });} export default function App() { const [loading, setLoading] = useState(true); const [items, setItems] = useState([]); useEffect(() => { fetchProducts().then((data) => { setItems(data); setLoading(false); }); }, []); if (loading) return <ActivityIndicator style={{ margin: 30 }} color="#E8820C" />; return ( <FlatList style={{ padding: 10 }} data={items} keyExtractor={(it) => it.id} renderItem={({ item }) => ( <View style={styles.row}> <Text>{item.name}</Text> <Text style={styles.p}>{item.price} ر.س</Text> </View> )} /> );} const styles = StyleSheet.create({ row: { flexDirection: 'row', justifyContent: 'space-between', padding: 12, borderBottomWidth: 1, borderColor: '#eee' }, p: { color: '#E8820C', fontWeight: 'bold' },});
لاحظ المؤشّر أولًا ثم ظهور القائمة. ActivityIndicator مكوّنٌ جاهز لحالة التحميل.
٣معالجة الخطأ
الشبكة قد تفشل. نلتقط الخطأ ونعرض رسالةً بدل انهيار التطبيق:
useEffect(() => { let alive = true; fetchProducts() .then((data) => { if (alive) { setItems(data); setLoading(false); } }) .catch(() => { if (alive) { setError(true); setLoading(false); } }); return () => { alive = false; }; // لا تحدّث مكوّنًا أُزيل}, []);
المتغيّر alive يمنع تحديث الحالة بعد إزالة المكوّن (خطأ شائع). وcatch يحوّلنا لحالة الخطأ.
توقّع وتحقّق
ما الحالات الثلاث لجلب البيانات؟
متى نجلب البيانات عادةً؟
مكوّن مؤشّر التحميل الجاهز:
لمعالجة فشل الشبكة نستخدم:
الخلاصة
- جلب البيانات ثلاث حالات: تحميل، نجاح، خطأ — لكلٍّ واجهة.
- نجلب مرّةً في
useEffect []ونحفظloadingوdata. catchيعالج فشل الشبكة؛ وعلَمٌ مثلaliveيمنع تحديث مكوّنٍ أُزيل.
تحدٍّ برمجي: جلب أسماء
متقدماجلب قائمةً من مصدرٍ محاكى واعرض عددها.
المطلوب:
- دالّة محاكاة تُرجع وعدًا بمصفوفة أسماء (3 على الأقل).
- اجلبها في
useEffectمرّةً، واحفظها في حالة. <Text testID="count">يعرض عددها بعد الجلب.
👀 اعرض الناتج المطلوب (وجرّبه)
✓ التحقق الآلي من حلّك:
- الكود يعمل بلا أخطاء في Console
- العدّاد يظهر 3 بعد الجلب
- استُخدم useEffect
- جُلبت البيانات بوعد then
💡 تلميح 1
اجلب في
useEffect(() => {{ fetchNames().then(setNames); }}, []).💡 تلميح 2
العدّاد:
{names.length}.حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.
import { useState, useEffect } from 'react';import { View, Text, StyleSheet } from 'react-native'; function fetchNames() { return Promise.resolve(['سعود', 'نورة', 'خالد']);} export default function App() { const [names, setNames] = useState([]); useEffect(() => { fetchNames().then(setNames); }, []); return ( <View style={{ padding: 20 }}> <Text testID="count" style={styles.c}>العدد: {names.length}</Text> </View> );} const styles = StyleSheet.create({ c: { fontSize: 22, fontWeight: 'bold', color: '#E8820C' },});