0Pricing
React Native Academy · درس

تمرير البيانات باستخدام Props

عرّف مكوّنًا قابلًا لإعادة الاستخدام يقبل props، ومرّر القيم من مكوّن أب، واستخدم PropTypes أو TypeScript لتوثيق أشكال props المتوقعة.

تمرير البيانات باستخدام Props درس مجاني في React Native Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في React Native Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة React Native Academy 4 دروس في المجموع.

بعض أجزاء هذا الدرس لم تُترجم بعد وتظهر باللغة الإنجليزية.

What Are Props?

Props (short for properties) are the mechanism React components use to receive input from their parent. A parent component renders a child component and passes data to it through attributes in JSX — just like HTML attributes, but for any JavaScript value including strings, numbers, objects, arrays, and functions. The child component receives all its props as a single object argument. Props flow in one direction: from parent down to child. A child component can never modify its own props — they are read-only.

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

// Child component receives props
function Greeting({ name, age }) {
  return (
    <View>
      <Text>Hello, {name}!</Text>
      <Text>You are {age} years old.</Text>
    </View>
  );
}

// Parent passes data through props
export default function App() {
  return (
    <View style={{ flex: 1, padding: 24 }}>
      <Greeting name='Alice' age={30} />
      <Greeting name='Bob' age={25} />
    </View>
  );
}

Destructuring Props

You can access props in two ways: as a single props object (function Card(props) { props.title }) or by destructuring directly in the function signature (function Card({ title, subtitle })). Destructuring is the preferred style because it makes the accepted props explicit and the code more concise. You can also provide default values for props in the destructuring syntax: { size = 16, color = '#333' }. This means the prop is optional and falls back to the default when not provided by the parent.

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

// Destructuring with default values
function Badge({ label, color = '#4f86f7', size = 14 }) {
  return (
    <View style={[styles.badge, { backgroundColor: color }]}>
      <Text style={[styles.text, { fontSize: size }]}>{label}</Text>
    </View>
  );
}

export default function App() {
  return (
    <View style={{ gap: 8, padding: 16 }}>
      <Badge label='New' />                        {/* uses defaults */}
      <Badge label='Sale' color='#e74c3c' />       {/* custom color */}
      <Badge label='Pro' color='#2ecc71' size={12} />
    </View>
  );
}

const styles = StyleSheet.create({
  badge: { paddingHorizontal: 10, paddingVertical: 4, borderRadius: 12, alignSelf: 'flex-start' },
  text: { color: '#fff', fontWeight: 'bold' },
});

Passing Different Prop Types

Props can carry any JavaScript value. Strings don't need curly braces (title='Hello'), but all other types do — numbers (count={5}), booleans (disabled={true} or just disabled), objects (style={{ color: 'red' }}), arrays (items={[1,2,3]}), and functions (onPress={handlePress}). Passing functions as props is how children communicate back to parents — the parent defines the function and passes it down; the child calls it when something happens. This pattern is central to React's unidirectional data flow.

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

// onPress is a function prop — callback from child to parent
function ConfirmButton({ label, count, disabled, onPress }) {
  return (
    <TouchableOpacity
      onPress={onPress}
      disabled={disabled}
      style={{ opacity: disabled ? 0.4 : 1, backgroundColor: '#4f86f7', padding: 12, borderRadius: 8 }}
    >
      <Text style={{ color: '#fff', fontWeight: 'bold' }}>
        {label} ({count})
      </Text>
    </TouchableOpacity>
  );
}

export default function App() {
  return (
    <View style={{ padding: 24 }}>
      <ConfirmButton
        label='Add to Cart'
        count={3}
        disabled={false}
        onPress={() => console.log('Added!')}
      />
    </View>
  );
}

The children Prop

The special children prop holds whatever you put between the opening and closing tags of a component. This enables composition — you can build wrapper or container components that apply layout, styling, or behavior to any content nested inside. React Native's View, ScrollView, and TouchableOpacity all work this way. Define your own wrapper components with children to create reusable layout shells like cards, modals, and screen templates.

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

// A Card wrapper that applies consistent styling
function Card({ children, style }) {
  return (
    <View style={[styles.card, style]}>
      {children}
    </View>
  );
}

export default function App() {
  return (
    <View style={{ padding: 16 }}>
      <Card>
        {/* Any content can go inside the Card */}
        <Text>Title</Text>
        <Text>Subtitle</Text>
        <Image source={{ uri: 'https://picsum.photos/200' }} style={{ width: 200, height: 100 }} />
      </Card>
    </View>
  );
}

const styles = StyleSheet.create({
  card: { backgroundColor: '#fff', borderRadius: 16, padding: 16, elevation: 3, marginBottom: 12 },
});

Prop Spreading with the Spread Operator

Use the JavaScript spread operator to pass all properties of an object as individual props. This is convenient when you have a data object with many fields: <UserCard {...user} /> is equivalent to <UserCard id={user.id} name={user.name} avatar={user.avatar} />. The spread pattern is common when rendering items from an array with .map(). Be careful not to spread unknown props onto native components (View, Text) — they will trigger warnings or be silently ignored. Only spread props onto custom components that you control.

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

const users = [
  { id: '1', name: 'Alice', role: 'Admin' },
  { id: '2', name: 'Bob', role: 'User' },
];

function UserRow({ name, role }) {
  return (
    <View style={{ padding: 12, borderBottomWidth: 1, borderBottomColor: '#eee' }}>
      <Text style={{ fontWeight: 'bold' }}>{name}</Text>
      <Text style={{ color: '#666' }}>{role}</Text>
    </View>
  );
}

export default function UserList() {
  return (
    <View>
      {users.map(user => (
        <UserRow key={user.id} {...user} />
      ))}
    </View>
  );
}

TypeScript Props with Interfaces

In TypeScript, define the shape of your props using an interface or type. This catches errors at compile time — passing a number where a string is expected, or forgetting a required prop, shows an error before you even run the app. Mark optional props with ?. TypeScript also enables editor auto-complete: when you type <MyComponent in JSX, your editor lists all available props and their types. This is one of the biggest productivity benefits of using TypeScript in React Native projects.

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

interface ProductCardProps {
  id: string;
  name: string;
  price: number;
  imageUri: string;
  badge?: string;      // optional
  onPress?: () => void; // optional callback
}

export default function ProductCard({
  name,
  price,
  imageUri,
  badge,
  onPress,
}: ProductCardProps) {
  return (
    <View style={styles.card}>
      <Text style={styles.name}>{name}</Text>
      <Text style={styles.price}>${price.toFixed(2)}</Text>
      {badge && <Text style={styles.badge}>{badge}</Text>}
    </View>
  );
}

const styles = StyleSheet.create({
  card: { padding: 16, backgroundColor: '#fff', borderRadius: 12 },
  name: { fontSize: 16, fontWeight: '600' },
  price: { fontSize: 18, color: '#4f86f7', fontWeight: 'bold', marginTop: 4 },
  badge: { color: '#e74c3c', fontSize: 12, marginTop: 4 },
});

Callback Props: Child to Parent Communication

Since data flows only downward in React, a child communicates back to its parent by calling a callback function passed as a prop. The parent defines the function (what should happen when the user acts), passes it to the child, and the child calls it at the right time. This is the standard pattern for buttons, form inputs, and list items — the child component doesn't care about what happens next (no business logic), it just calls the prop function with any relevant data.

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

// Child: knows nothing about business logic
function DeleteButton({ itemId, onDelete }) {
  return (
    <TouchableOpacity onPress={() => onDelete(itemId)}>
      <Text style={{ color: 'red' }}>Delete</Text>
    </TouchableOpacity>
  );
}

// Parent: owns the logic and state
export default function ItemList() {
  const [items, setItems] = React.useState(['Apple', 'Banana', 'Cherry']);

  function handleDelete(index) {
    setItems(prev => prev.filter((_, i) => i !== index));
  }

  return (
    <View>
      {items.map((item, i) => (
        <View key={i} style={{ flexDirection: 'row', justifyContent: 'space-between', padding: 12 }}>
          <Text>{item}</Text>
          <DeleteButton itemId={i} onDelete={handleDelete} />
        </View>
      ))}
    </View>
  );
}

Avoiding Prop Drilling

Prop drilling happens when you pass props through several intermediate components that don't need the data themselves — they just forward it deeper. For example, passing a user object from App → Screen → Section → Card → Avatar just to display the avatar image. This becomes difficult to maintain as the hierarchy grows. The solution is to use Context (for global or shared data like the logged-in user or theme) or to colocate state closer to the components that need it. Don't over-engineer early — a few levels of prop passing is perfectly fine.

// Prop drilling anti-pattern (not ideal when deep)
function App() {
  const user = { name: 'Alice', avatar: 'https://...' };
  return <Screen user={user} />;
}
function Screen({ user }) {
  return <Section user={user} />; // just passing through
}
function Section({ user }) {
  return <Avatar user={user} />; // just passing through
}
function Avatar({ user }) {
  return <Image source={{ uri: user.avatar }} style={{ width: 40, height: 40 }} />; // actually uses it
}

// Solution: Context (covered later)
// Or: pass only the specific value needed at each level
function Section({ avatarUri }) { ... }
function Avatar({ uri }) { ... }

Immutable Props: Why Children Cannot Modify Them

A core rule of React: a component must never modify its own props. Props are the parent's data passed to the child for display — they are owned by the parent. If a child needs to change a value, it should call a callback prop to tell the parent, and the parent updates its state. The parent then passes the updated value back down as a new prop. This unidirectional flow makes data changes predictable and debuggable — you always know where state lives and who controls it.

// WRONG: Never mutate props directly
function Counter({ count }) {
  // This is an error — do not do this:
  // count = count + 1;

  return <Text>{count}</Text>;
}

// CORRECT: Notify parent via callback, parent updates state
function Counter({ count, onIncrement }) {
  return (
    <TouchableOpacity onPress={onIncrement}>
      <Text>{count}</Text>
    </TouchableOpacity>
  );
}

// In the parent:
const [count, setCount] = useState(0);
<Counter count={count} onIncrement={() => setCount(c => c + 1)} />

Real-World Props Example: Feed Post

Apply all prop concepts to a realistic social media feed post component. The component receives data from the feed list (text, image, author, like count, timestamp) and callback functions for user interactions (like, comment, share). The component is purely presentational — all data comes in through props, all interactions flow out through callback props. This design makes the FeedPost component easy to test in isolation and reuse across different feed types (home feed, profile feed, search results).

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

export default function FeedPost({
  author, avatarUri, imageUri, caption, likes, timeAgo,
  onLike, onComment, onShare
}) {
  return (
    <View style={styles.post}>
      <View style={styles.header}>
        <Image source={{ uri: avatarUri }} style={styles.avatar} />
        <View>
          <Text style={styles.author}>{author}</Text>
          <Text style={styles.time}>{timeAgo}</Text>
        </View>
      </View>
      <Image source={{ uri: imageUri }} style={styles.postImage} />
      <Text style={styles.caption}>{caption}</Text>
      <View style={styles.actions}>
        <TouchableOpacity onPress={onLike}><Text>❤️ {likes}</Text></TouchableOpacity>
        <TouchableOpacity onPress={onComment}><Text>💬 Comment</Text></TouchableOpacity>
        <TouchableOpacity onPress={onShare}><Text>🔗 Share</Text></TouchableOpacity>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  post: { backgroundColor: '#fff', marginBottom: 8 },
  header: { flexDirection: 'row', alignItems: 'center', padding: 12, gap: 10 },
  avatar: { width: 40, height: 40, borderRadius: 20 },
  author: { fontWeight: 'bold', fontSize: 14 },
  time: { fontSize: 12, color: '#999' },
  postImage: { width: '100%', aspectRatio: 1 },
  caption: { padding: 12, fontSize: 14 },
  actions: { flexDirection: 'row', gap: 20, padding: 12, borderTopWidth: 1, borderTopColor: '#eee' },
});

Default Props with defaultProps

In addition to destructuring defaults, React components can define defaultProps as a static property — a plain object that specifies fallback values for each optional prop. When a prop is undefined (not passed by the parent), React substitutes the value from defaultProps. This pattern is older than destructuring defaults and works the same way, but is less common in modern TypeScript codebases where interface defaults are preferred. Both approaches achieve the same result: making optional props safe to consume without null checks.

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

function Avatar({ size, color, initials }) {
  return (
    <View style={[styles.circle, { width: size, height: size, borderRadius: size / 2, backgroundColor: color }]}>
      <Text style={styles.initials}>{initials}</Text>
    </View>
  );
}

// defaultProps: applied when prop is undefined
Avatar.defaultProps = {
  size: 48,
  color: '#4f86f7',
};

// Usage:
<Avatar initials='AB' />                    // uses defaults: 48px blue
<Avatar initials='CD' size={64} />          // custom size, default color
<Avatar initials='EF' size={32} color='#e74c3c' />  // all custom

const styles = StyleSheet.create({
  circle: { alignItems: 'center', justifyContent: 'center' },
  initials: { color: '#fff', fontWeight: 'bold', fontSize: 16 },
});

Quick Check

Test your understanding of React Native Mobile Development concepts from this lesson.

Lesson Recap

In this lesson you learned: props are read-only values passed from parent to child in one direction, callback function props allow children to notify parents of user actions, and TypeScript interfaces document and enforce the expected prop shape at compile time. Next up we explore managing changing values inside components with the useState hook.

الأسئلة الشائعة

هل درس «تمرير البيانات باستخدام Props» مجاني؟

نعم — نص درس «تمرير البيانات باستخدام Props» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة React Native Academy، انتقل إلى CoddyKit PRO. تتضمن دورة React Native Academy 4 دروس في المجموع.

ماذا ستتعلم في «تمرير البيانات باستخدام Props»؟

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

هل أحتاج إلى خبرة سابقة لأبدأ React Native Academy؟

لا تُشترط خبرة سابقة. React Native Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.

كم من الوقت يستغرق درس «تمرير البيانات باستخدام Props»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس React Native Academy هذا؟

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

جميع الدروس في هذه الدورة

  1. تمرير البيانات باستخدام Props
  2. إدارة حالة المكوّن باستخدام useState
  3. رفع الحالة إلى الأعلى
  4. إنشاء تطبيق عدّاد تفاعلي
← العودة إلى React Native Academy