React Native Academy · درس

قراءة حالة المتجر وتحديثها في المكوّنات

يمكنك الوصول إلى قيم المتجر وإجراءاته في مكوّن React Native باستخدام hook ‏useStore، وفعّل التحديثات من نقرات الأزرار أو عمليات إرسال النماذج.

الدرس 2 من 413 خطوة

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

ربط مكوّن بـ Zustand

إن استخدام store من Zustand داخل مكوّن بسيط مثل استدعاء hook الخاص بالstore في أعلى الدالة. والـ hook هو الكائن نفسه الذي يعيده create. يمكنك تمرير selector لاختيار قيم محددة، أو قراءة actions مباشرةً. لا حاجة إلى استيراد useSelector أو useDispatch — إذ يتولى hook الخاص بالstore كل شيء.

import { useCounterStore } from '../stores/counterStore';

export default function CounterScreen() {
  const count = useCounterStore((state) => state.count);
  const increment = useCounterStore((state) => state.increment);
  const decrement = useCounterStore((state) => state.decrement);

  return (
    <View style={styles.container}>
      <Text style={styles.count}>{count}</Text>
      <Button title='Increment' onPress={increment} />
      <Button title='Decrement' onPress={decrement} />
    </View>
  );
}

اختيار قيم متعددة بكفاءة

لقراءة قيم متعددة من store في Zustand من دون التسبب في عمليات إعادة تصيير مفرطة، لديك خياران. يمكنك استدعاء hook الخاص بالstore عدة مرات باستخدام selectors منفصلة، إذ يكون كل اشتراك مستقلًا، أو يمكنك إرجاع كائن واستخدام مساعد المساواة shallow لمقارنة كل خاصية على حدة.

import { shallow } from 'zustand/shallow';

// Option 1: multiple hooks (preferred for 2-3 values)
const count = useCounterStore((s) => s.count);
const status = useCounterStore((s) => s.status);

// Option 2: shallow equality for multiple values at once
const { count, status } = useCounterStore(
  (s) => ({ count: s.count, status: s.status }),
  shallow
);

تشغيل التحديثات من ضغطات الأزرار

إن actions في Zustand مجرد دوال JavaScript عادية، لذا يمكنك تمريرها مباشرةً إلى معالجات أحداث React Native مثل onPress. لا تحتاج إلى إرسال كائن action أو استدعاء دالة مساعدة — ما عليك سوى استدعاء الدالة. يجعل ذلك كود المكوّن أوضح وأسهل قراءةً مقارنةً بنمط dispatch في Redux.

export default function TodoInput() {
  const [text, setText] = React.useState('');
  const addTodo = useTodoStore((state) => state.addTodo);

  const handleAdd = () => {
    if (text.trim()) {
      addTodo(text.trim());
      setText('');
    }
  };

  return (
    <View>
      <TextInput value={text} onChangeText={setText} placeholder='New todo' />
      <Button title='Add' onPress={handleAdd} />
    </View>
  );
}

قراءة حالة store في FlatList

تعمل متاجر Zustand بشكل طبيعي مع FlatList في React Native. اختر المصفوفة من store ومرّرها إلى الخاصية data. لا يعيد Zustand تصيير هذا المكوّن إلا عندما يتغير مرجع المصفوفة، ويحدث ذلك عندما تستبدل action الخاصة بالstore مصفوفة العناصر أو تعدّلها وتعيد مرجعًا جديدًا عبر set.

export default function TodoList() {
  const todos = useTodoStore((state) => state.todos);
  const removeTodo = useTodoStore((state) => state.removeTodo);

  return (
    <FlatList
      data={todos}
      keyExtractor={(item) => item.id}
      renderItem={({ item }) => (
        <TouchableOpacity onPress={() => removeTodo(item.id)}>
          <Text>{item.text}</Text>
        </TouchableOpacity>
      )}
    />
  );
}

تحديث الحالة المتداخلة

عندما تحتوي حالة store على كائنات متداخلة، مرّر دالة إلى set وانشر المستوى المتداخل الذي تريد تحديثه. من دون النشر، سيستبدل set المفتاح الموجود في المستوى الأعلى بالكامل، مما يؤدي إلى حذف الخصائص المتداخلة الأخرى. وبدلًا من ذلك، يمكنك استخدام Immer middleware لتعديل القيم المتداخلة مباشرةً، كما تناولنا في الدرس السابق.

export const useProfileStore = create<ProfileStore>((set) => ({
  profile: {
    name: '',
    bio: '',
    avatar: null,
  },
  updateName: (name: string) =>
    set((state) => ({ profile: { ...state.profile, name } })),
  updateBio: (bio: string) =>
    set((state) => ({ profile: { ...state.profile, bio } })),
}));

تحديثات واجهة المستخدم التفاؤلية

تُحسّن التحديثات التفاؤلية الأداء المتصوَّر من خلال تحديث واجهة المستخدم فورًا عندما ينفّذ المستخدم إجراءً، قبل انتظار استجابة الخادم. في Zustand، استدعِ set لتحديث الحالة فورًا، ثم أجرِ استدعاء API. وإذا فشل استدعاء API، فاستدعِ set مرة أخرى لإعادة الحالة إلى قيمتها السابقة.

toggleLike: async (postId: string) => {
  // Update UI immediately
  set((state) => ({
    posts: state.posts.map((p) =>
      p.id === postId ? { ...p, liked: !p.liked } : p
    ),
  }));
  try {
    await api.toggleLike(postId);
  } catch {
    // Revert on failure
    set((state) => ({
      posts: state.posts.map((p) =>
        p.id === postId ? { ...p, liked: !p.liked } : p
      ),
    }));
  }
}

إعادة ضبط حالة store

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

const initialState = { user: null, token: null, isLoggedIn: false };

export const useAuthStore = create<AuthStore>((set) => ({
  ...initialState,
  login: (user, token) => set({ user, token, isLoggedIn: true }),
  logout: () => set(initialState), // restore all fields to initial values
}));

مراقبة تغييرات store باستخدام subscribe

يتيح لك useStore.subscribe الاستجابة لتغييرات store خارج مكوّنات React — مثل الكتابة إلى AsyncStorage كلما تغيّرت الإعدادات، أو تسجيل أحداث التحليلات عند حدوث انتقالات محددة في الحالة. ألغِ الاشتراك دائمًا داخل دالة التنظيف لتجنب تسرّبات الذاكرة.

useEffect(() => {
  const unsubscribe = useSettingsStore.subscribe(
    (state) => state.theme,
    (theme) => {
      console.log('Theme changed to:', theme);
      AsyncStorage.setItem('theme', theme);
    }
  );
  return unsubscribe; // cleanup on unmount
}, []);

مشاركة الحالة بين الشاشات

بما أن متاجر Zustand توجد خارج أي شجرة مكوّنات، فهي مثالية لمشاركة الحالة بين الشاشات من دون تمرير الخصائص أو استخدام معاملات React Navigation. تحدّث الشاشة A الـ store، وتقرأ الشاشة B منه — وترى كلتاهما الحالة نفسها مباشرةً وبشكل حي. وهذا أبسط بكثير من تمرير البيانات عبر معاملات المسارات في هياكل التنقل العميقة.

// ScreenA.tsx — writes to store
const setSelectedProduct = useProductStore((s) => s.setSelected);
<Button title='View Details' onPress={() => {
  setSelectedProduct(product);
  navigation.navigate('ProductDetail');
}} />

// ScreenB.tsx — reads from store
const product = useProductStore((s) => s.selected);

اختبار actions الخاصة بـ Zustand store

يُعد اختبار store في Zustand مباشرًا، لأنه لا يعتمد على React. استورد store، واستدعِ actions مباشرةً على getState()، ثم تحقّق من القيم المحدّثة. يمكنك أيضًا إعادة ضبط store بين الاختبارات باستدعاء reset action أو الكتابة فوق الحالة باستخدام setState لضمان عزل الاختبارات.

import { useCounterStore } from '../counterStore';

before Each(() => {
  useCounterStore.getState().reset();
});

test('increment increases count by 1', () => {
  useCounterStore.getState().increment();
  expect(useCounterStore.getState().count).toBe(1);
});

test('reset brings count back to 0', () => {
  useCounterStore.setState({ count: 5 });
  useCounterStore.getState().reset();
  expect(useCounterStore.getState().count).toBe(0);
});

حالة النماذج باستخدام Zustand

يمكن لـ Zustand إدارة حالة نموذج تحتاج إلى مشاركتها بين شاشات متعددة ضمن مسار نموذج متعدد الخطوات. خزّن قيم الحقول ورقم الخطوة الحالية في store، ودَع كل شاشة تقرأ حقولها الخاصة وتحدّثها فقط. عند الإرسال النهائي، يمكنك الوصول إلى بيانات النموذج الكاملة من getState() داخل معالج الإرسال.

export const useFormStore = create<FormStore>((set) => ({
  step: 1,
  name: '',
  email: '',
  password: '',
  setField: (field, value) => set({ [field]: value }),
  nextStep: () => set((state) => ({ step: state.step + 1 })),
  reset: () => set({ step: 1, name: '', email: '', password: '' }),
}));

اختبار سريع

اختبر مدى فهمك لمفاهيم React Native Mobile Development التي تناولها هذا الدرس.

مراجعة الدرس

تعلّمت في هذا الدرس أن استدعاء hook الخاص بالstore باستخدام selector يتيح للمكوّن الاشتراك بكفاءة في الحالة التي يحتاج إليها فقط، وأن actions في Zustand مجرد دوال عادية تمرّرها مباشرةً إلى معالجات الأحداث، وأن store.subscribe يتيح تنفيذ آثار جانبية مثل التخزين المؤقت أو التحليلات خارج دورة حياة React. بعد ذلك، سنستكشف حفظ حالة Zustand باستخدام AsyncStorage.

البدء مجانًا

تعلم JavaScript مع معلم ذكاء اصطناعي — مجانًا

اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.

الدورات
30
الدروس
120

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

هل درس «قراءة حالة المتجر وتحديثها في المكوّنات» مجاني؟

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

ماذا ستتعلم في «قراءة حالة المتجر وتحديثها في المكوّنات»؟

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

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

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

كم من الوقت يستغرق درس «قراءة حالة المتجر وتحديثها في المكوّنات»؟

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

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

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

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

  1. إنشاء Zustand Store
  2. قراءة حالة المتجر وتحديثها في المكوّنات
  3. حفظ حالة Zustand باستخدام AsyncStorage
  4. نمط Slices والتكامل مع Devtools
← العودة إلى React Native Academy