أساسيات React Native: لبناء تطبيق حقيقي

أساسيات React Native: لبناء تطبيق حقيقي

React Native ليست مجرد تقنية أخرى في عالم تطوير التطبيقات، بل هي بوابة حقيقية تسمح لك بأن تبني تطبيقات تعمل على Android و iOS من قاعدة كود واحدة تقريبًا، ومن دون أن تضطر إلى إعادة كتابة كل شيء مرتين. وهذه الفكرة وحدها كافية لتجعل الكثير من المطورين يقفون أمامها باهتمام كبير، خصوصًا عندما يدركون أن المسألة ليست فقط في تقليل الوقت والتكلفة، بل أيضًا في الحفاظ على تجربة تطوير مريحة، مرنة، وقابلة للتوسع. الجميل في React Native أنها لا تطلب منك أن تنسى كل ما تعرفه عن تطوير الويب إن كنت قادمًا من React، بل على العكس، هي تستثمر معرفتك السابقة وتحوّلها إلى قوة عملية في عالم الموبايل. لذلك، عندما تبدأ معها، ستشعر أحيانًا أنك في مكان مألوف، ثم فجأة تكتشف أنك تتعامل مع شاشة لمس، تنقلات، أذونات، وأداء مختلف تمامًا عن الويب، وهنا تبدأ المتعة الحقيقية.

العديد من المبتدئين يظنون أن React Native مجرد “React لكن على الهاتف”، وهذه فكرة مفيدة كبداية، لكنها ليست دقيقة بالكامل. نعم، هناك مفاهيم مشتركة مثل المكونات، الحالة، الخصائص، ودورة الحياة الحديثة عبر Hooks، لكن البيئة التي تعمل فيها مختلفة. في الويب أنت ترسم داخل DOM، أما هنا فأنت تبني واجهة باستخدام مكونات أصلية Native Components تُترجم إلى عناصر حقيقية على الجهاز. وهذا يعني أنك لا تتعامل مع متصفح، بل مع نظام تشغيل له قواعده الخاصة، وخصائصه الخاصة، وتحدياته الخاصة. لهذا السبب يحتاج من يدخل عالم React Native إلى عقلية عملية: لا يكتفي بحفظ الأكواد، بل يفهم لماذا يعمل الكود بهذه الطريقة، وكيف ينعكس ذلك على تجربة المستخدم، وعلى الأداء، وعلى قابلية الصيانة.

من أجمل الأشياء في React Native أنها تدفعك للتفكير في التطبيق كمنتج حي، وليس كمجرد ملفات وكلاسات ودوال. عندما تبدأ في بناء تطبيق حقيقي، ستدرك أن القيمة ليست في كتابة شاشة جميلة فقط، بل في رحلة المستخدم من أول لمسة إلى آخر خطوة. هل الواجهة واضحة؟ هل التنقل سهل؟ هل التحميل سريع؟ هل الرسائل المفهومة تظهر عند الخطأ؟ هل التطبيق يظل مستقرًا حين يكون الاتصال ضعيفًا؟ كل هذه الأسئلة ليست هامشية، بل هي جوهر العمل. React Native تمنحك الأدوات لتجاوب على هذه الأسئلة، لكنك أنت من يصنع التجربة النهائية. وهذا بالضبط ما يجعل تعلم الأساسيات أمرًا حاسمًا: إذا كانت القاعدة قوية، فكل ما تبنيه فوقها سيكون أكثر ثباتًا.

أول نقطة يجب أن تستقر في ذهنك هي أن React Native تعتمد على JavaScript أو TypeScript، وبالتالي هي صديقة جدًا للمطورين الذين لديهم خلفية في React أو حتى في JavaScript عامة. لكن لا تتعجل وتفترض أن كل شيء سيكون مطابقًا. ستواجه فرقًا في طريقة عرض العناصر، في التصميم، في إدارة اللمس، في إعداد البيئة، وفي الأدوات الخاصة بالبناء والتشغيل. لذلك من الأفضل منذ البداية أن تتعامل معها كبيئة جديدة تستفيد من معرفتك القديمة بدل أن تنقل توقعاتك من الويب كما هي. هذا الفرق الذهني الصغير يختصر على المبتدئ الكثير من الحيرة والإحباط.

عندما تبدأ مشروعًا جديدًا، غالبًا ستستخدم إما Expo أو React Native CLI. Expo مفيد جدًا للمبتدئين لأنه يخفف الكثير من تعقيدات الإعداد ويمنحك بداية أسرع. أما React Native CLI فهو يعطيك تحكمًا أوسع في المشروع، وهو مناسب أكثر حين تحتاج تخصيصات عميقة أو تكاملات Native محددة. كثير من المطورين يبدأون بـ Expo ثم ينتقلون إلى CLI عندما تكبر حاجاتهم، وهذا مسار طبيعي جدًا. المهم أن تدرك أن اختيار الأداة ليس “أفضلية مطلقة”، بل هو قرار ينسجم مع احتياجك الحالي. إن كنت تريد التعلم بسرعة وبناء أول تطبيقاتك بثقة، فالبداية مع Expo غالبًا ستشعرك براحة أكبر.

عند إنشاء مشروع باستخدام Expo، ستجد نفسك أمام بنية بسيطة ومباشرة. قد تبدأ بملف App.js أو App.tsx، ثم تضيف المكونات تدريجيًا، وتتعلم كيف تنظم الشاشات، وكيف تدير التنقل، وكيف تجلب البيانات من API، وكيف تعرض القوائم. في البداية قد يبدو كل شيء صغيرًا، لكن لا تنخدع بهذا الانطباع. هذه البساطة هي ما يجعل التعلم سلسًا، وهي أيضًا ما يسمح لك بالتقدم خطوة خطوة من دون أن تغرق في تفاصيل بنية النظام منذ اليوم الأول. ومن الناحية النفسية، هذا مهم جدًا، لأن المبتدئ يحتاج إلى إنجاز صغير ملموس يشعره أنه يتقدم فعلًا، وليس إلى صدمة إعدادات طويلة قبل أن يرى أول شاشة.

لنبدأ بأبسط مثال ممكن، وهو شاشة تعرض نصًا وزرًا، ثم عند الضغط على الزر يتغير النص. هذا المثال صغير جدًا، لكنه يحتوي على لب الفكرة: المكونات، الحالة، وإعادة الرسم عند التغيير.

import React, { useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';

export default function App() {
  const [message, setMessage] = useState('مرحبًا بك في React Native');

  return (
    <View style={styles.container}>
      <Text style={styles.title}>{message}</Text>

      <TouchableOpacity
        style={styles.button}
        onPress={() => setMessage('تم تغيير الرسالة بنجاح')}
      >
        <Text style={styles.buttonText}>اضغط هنا</Text>
      </TouchableOpacity>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 24,
    backgroundColor: '#f7f7f7',
  },
  title: {
    fontSize: 22,
    marginBottom: 20,
    textAlign: 'center',
  },
  button: {
    backgroundColor: '#111827',
    paddingVertical: 12,
    paddingHorizontal: 20,
    borderRadius: 10,
  },
  buttonText: {
    color: '#fff',
    fontSize: 16,
  },
});

هذا المثال البسيط يشرح لك شيئًا مهمًا جدًا: في React Native، الواجهة هي نتيجة للحالة. عندما تتغير الحالة message، يعاد رسم الجزء الذي يعتمد عليها. هذه الفكرة تبدو عادية في البداية، لكنها في الحقيقة من أقوى مفاهيم React كلها. بدلاً من أن تذهب إلى الشاشة وتغيّر النص يدويًا، أنت تغيّر البيانات، والواجهة تتكفل بالباقي. هذا الأسلوب يقلل الأخطاء، ويجعل التطبيق أكثر قابلية للفهم، ويقربك من طريقة التفكير الحديثة في تطوير الواجهات.

عندما ننظر إلى المكونات الأساسية في React Native، نجد أن View و Text و Image و ScrollView و TextInput و Pressable و FlatList تشكل العمود الفقري لأي تطبيق تقريبًا. View يشبه الحاوية، وText مخصص للنصوص، وImage للصور، وTextInput لحقل الإدخال، وPressable للتفاعل مع النقر أو اللمس، وFlatList لعرض القوائم بكفاءة. من المهم جدًا أن تتذكر أن النصوص لا تُعرض داخل View مباشرة كما قد تتوقع في الويب؛ النص يجب أن يكون داخل Text. هذا التفصيل الصغير يسبب كثيرًا من الأخطاء للمبتدئين، لكنه أيضًا من الأشياء التي تتعلمها بسرعة مع الممارسة.

مثال بسيط على إدخال المستخدم:

import React, { useState } from 'react';
import { View, Text, TextInput, StyleSheet } from 'react-native';

export default function App() {
  const [name, setName] = useState('');

  return (
    <View style={styles.container}>
      <Text style={styles.label}>اكتب اسمك:</Text>

      <TextInput
        style={styles.input}
        value={name}
        onChangeText={setName}
        placeholder="مثال: Hassan"
      />

      <Text style={styles.preview}>
        مرحبًا {name ? name : 'أيها الصديق'}
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 24,
    justifyContent: 'center',
    backgroundColor: '#fff',
  },
  label: {
    fontSize: 18,
    marginBottom: 10,
  },
  input: {
    borderWidth: 1,
    borderColor: '#d1d5db',
    borderRadius: 10,
    padding: 12,
    fontSize: 16,
    marginBottom: 16,
  },
  preview: {
    fontSize: 20,
    fontWeight: '600',
  },
});

هذا النوع من الأمثلة ليس مجرد تدريب على الشفرة، بل تدريب على الإحساس بالتفاعل. المستخدم يكتب، وأنت ترد فورًا. وهذه الاستجابة السريعة هي ما يجعل تطبيقات الموبايل ناجحة. الناس لا يحبون الواجهة البطيئة أو المترددة. كلما كان التفاعل واضحًا، شعر المستخدم أن التطبيق “يفهمه”، وهذا شعور مهم أكثر مما يظن البعض. لذلك، حين تتعلم React Native، لا تنشغل فقط بسؤال “كيف أكتب الكود؟”، بل اسأل أيضًا “كيف يشعر المستخدم أثناء استخدام هذا الكود؟”.

من أكثر المفاهيم التي يجب أن تفهمها مبكرًا مفهوم الـ Props والـ State. الـ Props هي بيانات تأتي من خارج المكون، بينما الـ State هي بيانات داخلية يملكها المكون نفسه. يمكنك أن تتخيل الـ Props كرسالة تصل إلى المكون، أما الـ State فهي ذاكرة المكون الخاصة. هذا التمييز أساسي جدًا، لأنه يساعدك على بناء مكونات قابلة لإعادة الاستخدام. مثلًا، زر واحد يمكن أن يستخدم في عدة شاشات مع نصوص وألوان مختلفة، وكل ذلك من خلال Props. أما الحالة فتكون مفيدة عندما يحتاج المكون إلى تذكر شيء خاص به، مثل قيمة حقل الإدخال أو حالة فتح قائمة منسدلة أو عدد النقرات.

مثال يوضح Props:

import React from 'react';
import { View, Text, StyleSheet } from 'react-native';

function WelcomeCard({ name, role }) {
  return (
    <View style={styles.card}>
      <Text style={styles.name}>{name}</Text>
      <Text style={styles.role}>{role}</Text>
    </View>
  );
}

export default function App() {
  return (
    <View style={styles.container}>
      <WelcomeCard name="Hassan" role="Full Stack Developer" />
      <WelcomeCard name="Sara" role="React Native Learner" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    padding: 20,
    backgroundColor: '#f9fafb',
  },
  card: {
    backgroundColor: '#fff',
    padding: 16,
    borderRadius: 12,
    marginBottom: 12,
    shadowColor: '#000',
    shadowOpacity: 0.08,
    shadowRadius: 8,
    elevation: 3,
  },
  name: {
    fontSize: 20,
    fontWeight: '700',
    marginBottom: 4,
  },
  role: {
    fontSize: 16,
    color: '#6b7280',
  },
});

الجميل في هذا المثال أنه يوضح كيف يمكن لمكون واحد أن يخدم أكثر من حالة. هذا هو جوهر التفكير المعياري في React Native. بدلاً من أن تكرر نفسك، تبني مكونات صغيرة قابلة لإعادة الاستخدام. هذه العقلية مفيدة جدًا في التطبيقات الكبيرة، لأنك كلما قللت التكرار، قلّت الأخطاء، وسهلت الصيانة، وأصبح توسيع التطبيق لاحقًا أمرًا أقل ألمًا. وفي الحقيقة، كثير من مشاكل التطبيقات لا تأتي من التكنولوجيا نفسها، بل من عدم تنظيمها من البداية.

أما بالنسبة إلى التنسيق، فـ React Native تستخدم StyleSheet أو أساليب CSS-in-JS بشكل مشابه، لكن بأسلوب خاص يناسب الموبايل. يجب أن تعرف أن خصائص CSS ليست كلها متاحة كما في الويب. لا يوجد مثلًا DOM، ولا media queries التقليدية، ولا بعض الخصائص الخاصة بالمتصفحات. ومع ذلك، لديك نظام قوي ومرن لتصميم الواجهات باستخدام Flexbox بشكل أساسي. وهذه نقطة مهمة جدًا: إن كنت تعرف Flexbox من الويب، فأنت تملك أداة قوية بالفعل. معظم التخطيطات في React Native يمكن حلها بـ flex properties مثل flexDirection, justifyContent, alignItems, وflex. لهذا السبب، من المفيد أن تفهم Flexbox بعمق، لأنه سيجعل تصميم الواجهات أسهل بكثير.

مثال بسيط على تخطيط باستخدام Flexbox:

import React from 'react';
import { View, Text, StyleSheet } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <View style={styles.box}>
        <Text style={styles.text}>1</Text>
      </View>
      <View style={styles.box}>
        <Text style={styles.text}>2</Text>
      </View>
      <View style={styles.box}>
        <Text style={styles.text}>3</Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'space-around',
    alignItems: 'center',
    backgroundColor: '#eef2ff',
  },
  box: {
    width: 70,
    height: 70,
    borderRadius: 12,
    backgroundColor: '#4f46e5',
    justifyContent: 'center',
    alignItems: 'center',
  },
  text: {
    color: '#fff',
    fontSize: 22,
    fontWeight: '700',
  },
});

عندما تبدأ في تنظيم الواجهة، ستلاحظ أن التصميم في React Native ليس مجرد زخرفة، بل جزء من المنطق. المساحات، الأحجام، المحاذاة، والترتيب تؤثر مباشرة على قابلية الاستخدام. أحيانًا يكون الخطأ في التطبيق ليس في منطق الكود، بل في أن العنصر موضوع في مكان يسبب ارتباكًا للمستخدم أو يعيق الضغط عليه بسهولة. لذلك، التفكير في التصميم يجب أن يكون حاضرًا منذ البداية، حتى لو كنت مطورًا أكثر من كونك مصممًا. لا تحتاج أن تكون فنانًا بصريًا، لكنك تحتاج أن تكون دقيقًا ومنتبهًا للتفاصيل.

واحدة من أهم الأدوات في التطبيقات الحقيقية هي القوائم. أغلب التطبيقات تعرض بيانات متكررة: منتجات، مقالات، رسائل، أصدقاء، طلبات، إشعارات. وهنا يأتي دور FlatList، وهي عادة أفضل من ScrollView عندما تكون لديك بيانات كثيرة أو ديناميكية، لأنها أكثر كفاءة. كثير من المبتدئين يضعون كل شيء داخل ScrollView ثم يندهشون من بطء الأداء عند زيادة عدد العناصر. لكن FlatList مبنية لتعرض فقط ما يحتاجه المستخدم حاليًا، وهذا فرق كبير في الأداء.

مثال على FlatList:

import React from 'react';
import { View, Text, FlatList, StyleSheet } from 'react-native';

const users = [
  { id: '1', name: 'Ali', city: 'Rabat' },
  { id: '2', name: 'Mariam', city: 'Casablanca' },
  { id: '3', name: 'Youssef', city: 'Tanger' },
  { id: '4', name: 'Imane', city: 'Nador' },
];

export default function App() {
  return (
    <View style={styles.container}>
      <FlatList
        data={users}
        keyExtractor={(item) => item.id}
        renderItem={({ item }) => (
          <View style={styles.item}>
            <Text style={styles.name}>{item.name}</Text>
            <Text style={styles.city}>{item.city}</Text>
          </View>
        )}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
    backgroundColor: '#f3f4f6',
  },
  item: {
    backgroundColor: '#fff',
    padding: 16,
    borderRadius: 12,
    marginBottom: 12,
  },
  name: {
    fontSize: 18,
    fontWeight: '700',
  },
  city: {
    fontSize: 15,
    color: '#6b7280',
    marginTop: 4,
  },
});

هذا المثال قد يبدو بسيطًا جدًا، لكنه في الحقيقة يمثل منطقًا كاملًا موجودًا في التطبيقات اليومية. القوائم ليست مجرد عرض بيانات، بل هي نقطة التقاء بين البيانات والتفاعل والتنظيم البصري. لاحقًا يمكنك إضافة سحب للتحديث، تحميل تدريجي، بحث، فلترة، صفحات تفاصيل، وإجراءات مباشرة من القائمة نفسها. وكل هذا يبدأ من فهمك للمبدأ الأساسي: كيف أُظهر عناصر كثيرة بطريقة أنيقة وسريعة ومُدارة جيدًا؟

واحدة من الفروق المهمة بين تطوير الويب وتطوير الموبايل في React Native هي التنقل بين الشاشات. في الويب لديك صفحات وروابط، أما في الموبايل فالتنقل غالبًا يكون عبر Stack Navigator أو Tab Navigator أو Drawer. مكتبة React Navigation هي الأشهر في هذا المجال، وهي تمنحك نظامًا مرنًا لبناء تدفق الشاشات. هنا لا بد أن تفهم أن “التنقل” ليس مجرد نقل المستخدم من مكان إلى آخر، بل هو جزء من البنية الذهنية للتطبيق. كيف يدخل المستخدم؟ أين يبدأ؟ كيف يعود؟ هل هناك زر رجوع؟ هل الانتقال منسجم أم مربك؟ هذه الأسئلة يجب أن تُحل بفكر واضح.

مثال مبسط للتنقل باستخدام Stack Navigator:

import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { View, Text, Button, StyleSheet } from 'react-native';

const Stack = createNativeStackNavigator();

function HomeScreen({ navigation }) {
  return (
    <View style={styles.screen}>
      <Text style={styles.title}>Home Screen</Text>
      <Button title="اذهب إلى التفاصيل" onPress={() => navigation.navigate('Details')} />
    </View>
  );
}

function DetailsScreen() {
  return (
    <View style={styles.screen}>
      <Text style={styles.title}>Details Screen</Text>
    </View>
  );
}

export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Details" component={DetailsScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}

const styles = StyleSheet.create({
  screen: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  title: {
    fontSize: 24,
    marginBottom: 16,
  },
});

عندما تبدأ بتطبيقات حقيقية، ستشعر أن التنقل هو العمود الفقري لتجربة المستخدم. الناس قد يغفرون لك بطئًا بسيطًا أحيانًا، لكنهم نادرًا ما يغفرون لهم ضياعًا في المسار أو واجهة لا يعرفون كيف يعودون منها. لهذا السبب، فهم التنقل مبكرًا يوفر عليك كثيرًا من المشاكل لاحقًا. ومن الجيد أن تتعامل مع الشاشات كمناطق منطقية في التطبيق، لا كمجرد ملفات منفصلة. كل شاشة يجب أن يكون لها دور واضح في رحلة المستخدم.

الـ Hooks من أكثر الأشياء التي جعلت React وReact Native أكثر بساطة وقوة. useState تعرفه بسرعة لأنه يدير الحالة، وuseEffect يساعدك في تنفيذ آثار جانبية مثل جلب البيانات أو الاشتراك في تحديثات، وuseRef يفيدك في حفظ مرجع ثابت، وuseMemo وuseCallback يساعدان أحيانًا في تحسين الأداء عندما يصبح التطبيق أكثر تعقيدًا. لكن لا تدخل إلى عالم الـ Hooks كأنها طقوس سحرية. ابدأ بما هو ضروري ثم توسع. كثير من المطورين الجدد يبالغون في استخدام كل Hook ممكن قبل أن يفهموا الحاجة الحقيقية إليه، ثم يجدون أنفسهم أمام كود معقد بدل أن يكون أسهل.

مثال على استخدام useEffect لجلب بيانات:

import React, { useEffect, useState } from 'react';
import { View, Text, ActivityIndicator, FlatList, StyleSheet } from 'react-native';

export default function App() {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function loadPosts() {
      try {
        const response = await fetch('https://jsonplaceholder.typicode.com/posts');
        const data = await response.json();
        setPosts(data.slice(0, 10));
      } catch (error) {
        console.log('Error fetching posts:', error);
      } finally {
        setLoading(false);
      }
    }

    loadPosts();
  }, []);

  if (loading) {
    return (
      <View style={styles.center}>
        <ActivityIndicator size="large" />
      </View>
    );
  }

  return (
    <View style={styles.container}>
      <FlatList
        data={posts}
        keyExtractor={(item) => String(item.id)}
        renderItem={({ item }) => (
          <View style={styles.card}>
            <Text style={styles.postTitle}>{item.title}</Text>
            <Text numberOfLines={2} style={styles.postBody}>
              {item.body}
            </Text>
          </View>
        )}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 16,
    backgroundColor: '#f9fafb',
  },
  center: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  card: {
    backgroundColor: '#fff',
    padding: 16,
    borderRadius: 12,
    marginBottom: 12,
  },
  postTitle: {
    fontSize: 18,
    fontWeight: '700',
    marginBottom: 6,
  },
  postBody: {
    color: '#4b5563',
    fontSize: 14,
  },
});

هذا المثال يفتح لك بابًا مهمًا جدًا: التعامل مع البيانات القادمة من الإنترنت. معظم التطبيقات الحديثة ليست معزولة، بل تعتمد على APIs. لذلك، تعلم جلب البيانات وعرضها ومعالجة حالات التحميل والخطأ ليس خيارًا إضافيًا، بل جزء من الأساسيات. المستخدم لا يريد أن يرى شاشة فارغة ولا يريد أن يشعر أن التطبيق عالق. هنا تظهر قيمة ActivityIndicator، ورسائل الخطأ، والمحاولات الذكية لإعادة الطلب، والتصرف عند انقطاع الاتصال. هذه التفاصيل الصغيرة تصنع الفارق بين تطبيق “يعمل” وتطبيق “مريح”.

وعندما تتكلم عن الأساسيات، لا يمكن تجاهل نماذج الإدخال Forms. تسجيل الدخول، إنشاء الحساب، تغيير كلمة المرور، إضافة منتج، إرسال تعليق، وكلها تعتمد على إدخال المستخدم. React Native توفر لك TextInput، لكن إدارة النموذج الجيد تحتاج أكثر من مجرد عرض حقل نص. أنت بحاجة إلى التحقق من البيانات، وإظهار الأخطاء بوضوح، ومنع الإرسال إذا كانت البيانات غير صحيحة، وتقديم تغذية راجعة سلسة للمستخدم. لا تجعل النموذج يبدو كحاجز، بل اجعله يبدو كخطوة طبيعية سهلة.

مثال لنموذج بسيط مع تحقق:

import React, { useState } from 'react';
import { View, Text, TextInput, Pressable, StyleSheet } from 'react-native';

export default function App() {
  const [email, setEmail] = useState('');
  const [error, setError] = useState('');

  const handleSubmit = () => {
    if (!email.includes('@')) {
      setError('الرجاء إدخال بريد إلكتروني صحيح');
      return;
    }

    setError('');
    alert('تم إرسال النموذج بنجاح');
  };

  return (
    <View style={styles.container}>
      <Text style={styles.label}>البريد الإلكتروني</Text>
      <TextInput
        style={styles.input}
        value={email}
        onChangeText={setEmail}
        keyboardType="email-address"
        autoCapitalize="none"
        placeholder="name@example.com"
      />

      {error ? <Text style={styles.error}>{error}</Text> : null}

      <Pressable style={styles.button} onPress={handleSubmit}>
        <Text style={styles.buttonText}>إرسال</Text>
      </Pressable>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    padding: 24,
    backgroundColor: '#fff',
  },
  label: {
    fontSize: 16,
    marginBottom: 8,
  },
  input: {
    borderWidth: 1,
    borderColor: '#d1d5db',
    borderRadius: 10,
    padding: 12,
    marginBottom: 8,
  },
  error: {
    color: '#dc2626',
    marginBottom: 12,
  },
  button: {
    backgroundColor: '#2563eb',
    padding: 14,
    borderRadius: 10,
    alignItems: 'center',
  },
  buttonText: {
    color: '#fff',
    fontWeight: '700',
  },
});

مع تقدمك، ستكتشف أن العمل الحقيقي في التطبيقات لا يكون في كتابة الأكواد الواضحة فقط، بل في التعامل مع الحالات التي لا تسير كما تتوقع. ماذا لو فشل الاتصال؟ ماذا لو لم يعد من الـ API أي بيانات؟ ماذا لو أرسل المستخدم إدخالًا غير صحيح؟ ماذا لو استغرق التحميل وقتًا طويلًا؟ هنا تظهر قيمة التصميم الجيد والمنطق الجيد وإدارة الحالة بصورة صحيحة. والفرق بين مطور مبتدئ ومطور جيد غالبًا لا يكون في أنه يعرف كل شيء، بل في أنه يتوقع المشاكل مبكرًا ويضع لها مكانًا في بنية التطبيق.

ومن المواضيع التي تستحق الاهتمام في React Native الأداء. في المراحل الأولى قد لا تشعر به لأنه لم يصبح مشكلة بعد، لكن كلما كبر التطبيق بدأت التفاصيل الصغيرة تتراكم. إعادة الرسم غير الضرورية، الصور الكبيرة، القوائم غير المحسنة، المكونات الثقيلة، والحسابات المتكررة كلها قد تجعل التطبيق أبطأ. لذلك من المهم أن تبني عادة التفكير بالأداء منذ البداية. ليس المطلوب أن تصبح مهووسًا به في أول يوم، ولكن أن تعرف أين تكون المخاطر، وكيف تتجنبها. استخدم FlatList بدلًا من ScrollView للقوائم الكبيرة، واستعمل التحسينات فقط عندما تحتاجها فعلًا، ولا تنس أن الجودة لا تعني فقط الجمال بل أيضًا السلاسة.

أحيانًا يسأل المبتدئ: هل يجب أن أتعلم TypeScript مع React Native؟ والإجابة العملية هي أن TypeScript أصبح خيارًا ممتازًا جدًا، بل في كثير من المشاريع الحديثة هو الخيار المفضل. TypeScript يضيف طبقة من الأمان والتنظيم، خصوصًا عندما يكبر المشروع أو يعمل عليه أكثر من شخص. بدل أن تكتشف الخطأ في وقت التشغيل، قد تلتقطه مبكرًا أثناء الكتابة. هذا لا يعني أن JavaScript سيئة، بل يعني أن TypeScript يساعدك على التقليل من الأخطاء وزيادة وضوح العقود بين المكونات والبيانات. إن كنت مبتدئًا تمامًا، يمكنك أن تبدأ بجافاسكربت ثم تنتقل إلى TypeScript بعد أن ترتاح للمفاهيم الأساسية، أو تبدأ به من البداية إذا كنت مستعدًا قليلًا للتحدي.

مثال صغير باستخدام TypeScript:

import React from 'react';
import { View, Text, StyleSheet } from 'react-native';

type UserCardProps = {
  name: string;
  age: number;
};

function UserCard({ name, age }: UserCardProps) {
  return (
    <View style={styles.card}>
      <Text style={styles.name}>{name}</Text>
      <Text style={styles.age}>العمر: {age}</Text>
    </View>
  );
}

export default function App() {
  return (
    <View style={styles.container}>
      <UserCard name="Ahmed" age={28} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    padding: 20,
    backgroundColor: '#f8fafc',
  },
  card: {
    backgroundColor: '#fff',
    padding: 16,
    borderRadius: 12,
  },
  name: {
    fontSize: 20,
    fontWeight: '700',
  },
  age: {
    fontSize: 16,
    marginTop: 4,
  },
});

هذا المثال القصير يكشف جانبًا مهمًا جدًا: كلما كان شكل البيانات واضحًا، صار الكود أوضح. وضوح البيانات ينعكس مباشرة على وضوح التطبيق. وعندما يكون كل شيء معرفًا بشكل جيد، يصبح التعاون بين المطورين أسهل، كما يصبح اكتشاف الأخطاء أبسط. لذلك لا تنظر إلى الأنواع على أنها تعقيد إضافي، بل على أنها لغة تنظيم تساعدك على التفكير بشكل أفضل.

من الجوانب التي يجب أن تعطيها اهتمامًا خاصًا أيضًا هي الصور والوسائط. التطبيقات الحديثة تعتمد كثيرًا على الصور، سواء في البروفايل، أو المنتجات، أو المقالات، أو المنشورات الاجتماعية. لكن الصور قد تكون مشكلة أيضًا إذا لم تُدار جيدًا. الصورة الكبيرة جدًا تستهلك ذاكرة، وتبطئ التحميل، وقد تؤثر على تجربة المستخدم بشكل واضح. لذلك من المهم أن تختار الصيغ المناسبة، وتحمّل الصور بشكل ذكي، وتعرض placeholders عند الحاجة، وتفكر في التحسين من ناحية الحجم والجودة معًا. أحيانًا لا يحتاج المستخدم إلى الصورة الأصلية كاملة، بل إلى نسخة مضغوطة تبدو ممتازة وتعمل بسلاسة.

مثال على صورة داخل التطبيق:

import React from 'react';
import { View, Image, Text, StyleSheet } from 'react-native';

export default function App() {
  return (
    <View style={styles.container}>
      <Image
        source={{ uri: 'https://images.unsplash.com/photo-1517841905240-472988babdf9' }}
        style={styles.image}
      />
      <Text style={styles.caption}>صورة تجريبية داخل React Native</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#fff',
    padding: 20,
  },
  image: {
    width: 250,
    height: 250,
    borderRadius: 20,
    marginBottom: 16,
  },
  caption: {
    fontSize: 16,
    color: '#374151',
  },
});

عندما تبني تطبيقًا فعليًا، ستدرك أن المستخدم لا يهتم كثيرًا بما إذا كان الكود أنيقًا في الخلفية بقدر ما يهتم بتجربة ملموسة: هل التطبيق سريع؟ هل كل شيء واضح؟ هل الرسائل مفهومة؟ هل العودة للخلف تعمل كما يتوقع؟ هل هناك ارتباك بصري؟ ولذلك، من المهم ألا تجعل اهتمامك محصورًا فقط في الجانب التقني الضيق. React Native ناجحة عندما تتحول من مجرد إطار عمل إلى وسيلة لبناء تجربة متكاملة.

ومن المهم أيضًا أن تتعلم إدارة الحالة على نطاق أوسع عندما تكبر التطبيقات. useState ممتاز للحالات الصغيرة، لكن عند وجود بيانات مشتركة بين شاشات كثيرة، قد تحتاج إلى Context API أو مكتبات إدارة حالة أخرى مثل Redux أو Zustand أو Jotai حسب حجم المشروع وطبيعته. لا تتسرع في استخدام مكتبة كبيرة فقط لأن اسمها مشهور. أحيانًا يكون Context كافيًا، وأحيانًا يكون الحل الأبسط هو الأفضل. القاعدة الذهبية هنا: اختر الأداة التي تحل المشكلة الحالية بأقل تعقيد ممكن، وليس الأداة التي تبدو أكثر إثارة.

مثال بسيط على Context:

import React, { createContext, useContext, useState } from 'react';
import { View, Text, Pressable, StyleSheet } from 'react-native';

const ThemeContext = createContext();

function ThemeProvider({ children }) {
  const [isDark, setIsDark] = useState(false);

  return (
    <ThemeContext.Provider value={{ isDark, setIsDark }}>
      {children}
    </ThemeContext.Provider>
  );
}

function ThemeButton() {
  const { isDark, setIsDark } = useContext(ThemeContext);

  return (
    <Pressable
      style={[styles.button, isDark && styles.buttonDark]}
      onPress={() => setIsDark(!isDark)}
    >
      <Text style={styles.buttonText}>
        {isDark ? 'تبديل إلى الوضع الفاتح' : 'تبديل إلى الوضع الداكن'}
      </Text>
    </Pressable>
  );
}

function Screen() {
  const { isDark } = useContext(ThemeContext);

  return (
    <View style={[styles.screen, isDark && styles.screenDark]}>
      <Text style={[styles.title, isDark && styles.titleDark]}>
        هذا مثال بسيط على Context
      </Text>
      <ThemeButton />
    </View>
  );
}

export default function App() {
  return (
    <ThemeProvider>
      <Screen />
    </ThemeProvider>
  );
}

const styles = StyleSheet.create({
  screen: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
    backgroundColor: '#fff',
  },
  screenDark: {
    backgroundColor: '#111827',
  },
  title: {
    fontSize: 18,
    marginBottom: 16,
    color: '#111827',
  },
  titleDark: {
    color: '#f9fafb',
  },
  button: {
    backgroundColor: '#2563eb',
    paddingVertical: 12,
    paddingHorizontal: 16,
    borderRadius: 10,
  },
  buttonDark: {
    backgroundColor: '#374151',
  },
  buttonText: {
    color: '#fff',
    fontWeight: '700',
  },
});

هذا المثال يفتح الباب لفهم أوسع لإدارة الحالة المشتركة. عندما تتشارك عدة مكونات نفس البيانات، يصبح تمرير البيانات يدويًا عبر كل مستوى أمرًا مرهقًا. هنا تأتي أدوات مثل Context لتجعل البنية أوضح. ومع ذلك، لا تُحمّل المشروع بأدوات كثيرة بلا داعٍ. الخبرة الحقيقية تأتي من معرفة متى تستخدم الشيء ومتى لا تستخدمه.

هناك جانب آخر لا يقل أهمية، وهو التعامل مع التخزين المحلي. كثير من التطبيقات تحتاج إلى حفظ بيانات بسيطة على الجهاز مثل التفضيلات، التوكن، الوضع الداكن، اللغة، أو آخر شاشة تمت زيارتها. يمكنك استخدام AsyncStorage للبيانات البسيطة، أو حلول أقوى حسب الحاجة. هذا النوع من التخزين مهم لأنه يجعل التطبيق يبدو “متذكرًا” للمستخدم، وهذه صفة يحبها الناس كثيرًا. المستخدم يحب أن يجد التطبيق في حالته المألوفة بدل أن يبدأ من الصفر في كل مرة يفتحه فيها.

كذلك، من المهم جدًا أن تتعلم كيفية الاختبار والتصحيح Debugging. أحيانًا يكون الخطأ واضحًا، وأحيانًا يكون مخفيًا في مكان لا تتوقعه. تعلم استخدام console.log بحكمة، وراقب الأخطاء، وافهم الرسائل التي يعطيها لك النظام، ولا تستعجل القفز إلى استنتاجات بعيدة. كثير من الأخطاء في React Native تكون بسيطة في أصلها، لكنها تبدو معقدة لأننا لم نقرأها جيدًا. لذلك، جزء مهم من الاحتراف ليس أن تتجنب الأخطاء تمامًا، بل أن تتعامل معها بهدوء ومنهجية.

من النصائح التي تستحق أن تُحفظ جيدًا: لا تبنِ التطبيق كاملًا من أول مرة. قسمه إلى أجزاء صغيرة. ابدأ بالتصميم العام، ثم التنقل، ثم عرض البيانات، ثم النماذج، ثم التخزين، ثم التحسين، ثم اللمسات النهائية. هذا الترتيب يساعدك على الإحساس بالإنجاز ويمنعك من التوهان. التطبيقات الكبيرة تبدو مخيفة عندما ننظر إليها ككتلة واحدة، لكنها تصبح أبسط بكثير عندما نجزئها إلى مراحل قابلة للإنجاز. وهذا درس يتجاوز React Native نفسها إلى طريقة التفكير البرمجية عمومًا.

من الناحية العملية، عندما تريد أن تصبح جيدًا في React Native، لا تكتفِ بالقراءة فقط. ابنِ تطبيقات صغيرة كثيرة. ابنِ تطبيق قائمة مهام، ثم تطبيق طقس، ثم تطبيق ملاحظات، ثم تطبيق متجر بسيط، ثم تطبيق أخبار، ثم تطبيق محادثة شكلية بواجهة فقط. كل مشروع صغير سيكشف لك جزءًا جديدًا من الصورة. التعلم هنا يشبه بناء بيت بالحجر، لا بلوحة واحدة. كل مشروع يضيف طبقة إلى فهمك، وكل خطأ تتعلم منه يصبح جزءًا من خبرتك.

مثال صغير لتطبيق قائمة مهام بسيط جدًا:

import React, { useState } from 'react';
import { View, Text, TextInput, Pressable, FlatList, StyleSheet } from 'react-native';

export default function App() {
  const [task, setTask] = useState('');
  const [tasks, setTasks] = useState([]);

  const addTask = () => {
    if (!task.trim()) return;
    setTasks([...tasks, { id: Date.now().toString(), title: task }]);
    setTask('');
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>قائمة المهام</Text>

      <TextInput
        style={styles.input}
        value={task}
        onChangeText={setTask}
        placeholder="أضف مهمة جديدة"
      />

      <Pressable style={styles.button} onPress={addTask}>
        <Text style={styles.buttonText}>إضافة</Text>
      </Pressable>

      <FlatList
        data={tasks}
        keyExtractor={(item) => item.id}
        style={styles.list}
        renderItem={({ item }) => (
          <View style={styles.taskItem}>
            <Text>{item.title}</Text>
          </View>
        )}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    backgroundColor: '#fff',
    justifyContent: 'center',
  },
  title: {
    fontSize: 28,
    fontWeight: '800',
    marginBottom: 20,
    textAlign: 'center',
  },
  input: {
    borderWidth: 1,
    borderColor: '#d1d5db',
    borderRadius: 10,
    padding: 12,
    marginBottom: 12,
  },
  button: {
    backgroundColor: '#111827',
    padding: 14,
    borderRadius: 10,
    alignItems: 'center',
  },
  buttonText: {
    color: '#fff',
    fontWeight: '700',
  },
  list: {
    marginTop: 20,
  },
  taskItem: {
    padding: 14,
    borderRadius: 10,
    backgroundColor: '#f3f4f6',
    marginBottom: 10,
  },
});

هذا المثال يجمع أكثر من مفهوم أساسي في مكان واحد: الحالة، الإدخال، القوائم، والإضافة الديناميكية. وهو يوضح كيف يمكن لأي فكرة بسيطة أن تتحول إلى تطبيق صغير كامل إن رُكبت بالشكل الصحيح. والأهم من ذلك كله أنه يذكرك بأن التعلم الحقيقي لا يأتي من متابعة الشروحات فقط، بل من إعادة بناء الفكرة بنفسك، ثم تعديلها، ثم كسرها أحيانًا، ثم إصلاحها. هذه الدورة هي التي تجعل المعرفة مستقرة في ذهنك.

ومن الجميل أيضًا أن تتعلم كتابة كود نظيف منذ البداية. سمِّ المتغيرات بشكل واضح، ووزع المكونات بطريقة منطقية، ولا تضع كل شيء داخل ملف واحد إن كان المشروع بدأ يكبر، وابتعد عن التكرار غير الضروري. حاول أن تجعل كل مكون له مسؤولية محددة. إذا كان المكون يعرض بطاقة منتج، فليعرض البطاقة فقط. إذا كان يعرض قائمة، فليتعامل مع القائمة فقط. إذا كان مسؤولًا عن جلب البيانات، فلا تخلط فيه كل شيء دفعة واحدة. التنظيم من البداية ليس رفاهية، بل هو استثمار يحميك لاحقًا من فوضى المشروع.

عندما تتعلم React Native جيدًا، ستصبح قادرًا على بناء أفكار كثيرة بسرعة أكبر. ستفهم كيف تربط الواجهة بالبيانات، وكيف تدير التنقل، وكيف تعرض القوائم، وكيف تبني النماذج، وكيف تتعامل مع الأخطاء، وكيف تحفظ البيانات محليًا، وكيف تفكر في الأداء. وهذا هو المعنى الحقيقي للأساسيات: ليست مجرد معلومات متفرقة، بل شبكة مترابطة من الفهم تمنحك الثقة. كلما كانت الأساسيات ثابتة، أصبح الإبداع أسهل. لأنك عندها لا تقف عند سؤال “كيف أفعل هذا؟” فقط، بل تبدأ في السؤال الأهم: “كيف أبني تجربة جميلة ومفيدة ومتماسكة؟”.

React Native قد تبدو في البداية كعالم كبير ومزدحم، لكن الحقيقة أنها تصبح لطيفة جدًا عندما تكسرها إلى أجزاء صغيرة. تعلم المكونات، ثم الحالة، ثم التنسيق، ثم القوائم، ثم التنقل، ثم الـ Hooks، ثم جلب البيانات، ثم النماذج، ثم التخزين، ثم الأداء. ومع كل خطوة ستلاحظ أنك لا تتعلم مجرد تقنية، بل تتعلم طريقة في التفكير. وهذه ربما أجمل هدية تقدمها لك React Native: أن تحوّل البرمجة من مجرد كتابة أوامر إلى بناء تجربة يعيشها المستخدم.

وفي النهاية، لا تنظر إلى الرحلة على أنها سباق. تعلم React Native ليس سباقًا إلى خط نهاية وهمي، بل مسار تتراكم فيه المهارة ببطء جميل. اليوم تفهم View وText، وغدًا تبني شاشة كاملة، وبعدها تنقل بين عدة شاشات، ثم تتعامل مع API، ثم تبدأ في تحسين الأداء، ثم تتعلم كيف تدير حالة أكبر، ثم تجد نفسك قادرًا على بناء تطبيق حقيقي بثقة. وهذا الشعور بالقدرة هو ما يستحق الجهد كله. عندما تصل إليه، ستعرف أن كل سطر كتبته في البداية، حتى لو بدا صغيرًا، كان يضع حجرًا في الطريق إلى مستوى أعلى من الاحتراف.

#React Native fundamentals #تعلم React Native #أساسيات React Native #React Native بالعربية #تطوير تطبيقات الهاتف #React Native tutorial Arabic