State Management ב-Mobile

📚 פיתוח Mobile ⏱️ 11 דק׳ 🎓 בינוני ✓ חינם לגמרי
State Management ב-Mobile

הבחירה שמשפיעה על כל render, State Management ב-React Native

אפליקציה עם עשרות אלפי משתמשים לא נכשלת בגלל עיצוב, היא נכשלת בגלל state שמסתדס. רינדרים מיותרים שמאיטים UI, נתונים מיושנים שמוצגים למשתמש, race conditions שמשחיתים data, כולם תוצאה ישירה של ארכיטקטורת state שגויה. ב-2026 הכלים הנכונים ברורים, אבל הבחירה ביניהם עדיין מבלבלת. השיעור הזה מכניס סדר.

נקודת הפתיחה: ב-React Native יש שני סוגי state שצריך לנהל בצורה שונה לחלוטין. Server state, נתונים שמגיעים מ-API: מוצרים, הזמנות, פרופיל משתמש. Client state, מצב ה-UI: האם menu פתוח, איזה tab נבחר, מה הועבר בחיפוש. הטעות הנפוצה ביותר היא לנהל את שניהם עם אותו כלי. כשמנהלים server state ב-Zustand, מקבלים caching ידני שתמיד יהיה פגום. כשמנהלים client state ב-Redux, מקבלים boilerplate ענק בלי שום ערך מוסף.

ארבעת הכלים, מה כל אחד עושה ומתי

כליגודלמתאים ללא מתאים ל
Zustand~1KBClient state גלובלי: cart, auth, UI preferencesServer state, אין caching מובנה
TanStack Query~13KBServer state: כל נתון שמגיע מ-APIUI state שלא נגיע ל-server
Jotai~2.5KBState אטומי עם interdependencies מורכבותLarge stores עם הרבה actions
Redux Toolkit~15KBEnterprise apps, teams גדולים, מורשתApps קטנים-בינוניים, overkill

כפי שמסכם dev.to: "In many apps, a hybrid approach works best: use Zustand for UI/local state and React Query for server state." זו המלצת ה-default לכל אפליקציה חדשה ב-2026.

Prompt לבחירת ארכיטקטורה, Claude כ-Architect

הפרומפט הזה מניח שClaude מבין את ה-scale שלכם ונותן recommendation שמותאם לכם, לא תשובה גנרית:

What state management should I use for React Native?
You are a React Native architect with production experience at scale. Our app has these characteristics: - 50K DAU, iOS + Android - 30 screens across 3 navigation stacks - Real-time order status via WebSocket - Offline support required: all read operations must work without network - Team of 2 developers, 6-month timeline - Stack: Expo SDK 52, TypeScript strict Compare Zustand vs Jotai for client state. Compare TanStack Query vs RTK Query for server state (we use REST). Recommend a combination, explain why, and show a concrete product-list feature using your recommendation.

כל שורה ב-prompt החזק עושה עבודה: expertise framing מושך ידע ארכיטקטורי לא רק syntax. scale משפיע על cache strategy. team size משפיע על learning curve. concrete example מונע תשובה abstract.

Zustand v5, מה השתנה ומה צריך לדעת

Zustand v5 יצא ב-Q4 2024 עם שינויים שבירים. אם אתם ב-v4, שימו לב:

דוגמה לstore נכון עם Zustand v5 ו-MMKV persistence:

// store/cartStore.ts
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import { useShallow } from 'zustand/react/shallow'; // v5, חובה לobject selectors
import { MMKV } from 'react-native-mmkv';

const storage = new MMKV();
const mmkvStorage = {
  getItem: (key: string) => storage.getString(key) ?? null,
  setItem: (key: string, value: string) => storage.set(key, value),
  removeItem: (key: string) => storage.delete(key),
};

type CartItem = {
  productId: string;
  name: string;
  price: number;
  quantity: number;
  inStock: boolean;
};

type CartStore = {
  items: CartItem[];
  promoCode: string | null;
  discount: number;
  addItem: (item: Omit<CartItem, 'quantity'>) => void;
  removeItem: (productId: string) => void;
  updateQuantity: (productId: string, qty: number) => void;
  applyPromoCode: (code: string, pct: number) => void;
  clearCart: () => void;
  total: () => number;
};

export const useCartStore = create<CartStore>()(
  persist(
    (set, get) => ({
      items: [],
      promoCode: null,
      discount: 0,
      addItem: (item) => set((s) => {
        const existing = s.items.find(i => i.productId === item.productId);
        if (existing) {
          return { items: s.items.map(i =>
            i.productId === item.productId ? { ...i, quantity: i.quantity + 1 } : i
          )};
        }
        return { items: [...s.items, { ...item, quantity: 1 }] };
      }),
      removeItem: (productId) => set((s) => ({
        items: s.items.filter(i => i.productId !== productId)
      })),
      updateQuantity: (productId, qty) => {
        if (qty <= 0) { get().removeItem(productId); return; }
        set((s) => ({ items: s.items.map(i =>
          i.productId === productId ? { ...i, quantity: qty } : i
        )}));
      },
      applyPromoCode: (code, pct) => set({ promoCode: code, discount: pct }),
      clearCart: () => set({ items: [], promoCode: null, discount: 0 }),
      total: () => {
        const { items, discount } = get();
        const sub = items.filter(i => i.inStock)
          .reduce((sum, i) => sum + i.price * i.quantity, 0);
        return sub * (1 - discount / 100);
      },
    }),
    {
      name: 'cart-storage',
      storage: createJSONStorage(() => mmkvStorage),
      version: 1, // חובה, מאפשר migration בעתיד
    }
  )
);

// Hook שמסתיר את ה-store internals
// שימוש ב-selectors נפרדים, לא object אחד גדול
export const useCart = () => ({
  items: useCartStore(s => s.items),
  total: useCartStore(s => s.total()),
  itemCount: useCartStore(s => s.items.reduce((n, i) => n + i.quantity, 0)),
  addItem: useCartStore(s => s.addItem),
  removeItem: useCartStore(s => s.removeItem),
  clearCart: useCartStore(s => s.clearCart),
});

TanStack Query ב-React Native, Offline ו-WebSocket

React Query לא מתנהג כמו בweb בתוך React Native. שני דברים חשובים שרוב מדריכים לא מזכירים:

Network reconnect אינו אוטומטי ב-mobile. ב-browser React Query מזהה reconnect לרשת ומבצע refetch. ב-React Native צריך להוסיף ידנית:

import NetInfo from '@react-native-community/netinfo';
import { onlineManager } from '@tanstack/react-query';

onlineManager.setEventListener(setOnline => {
  return NetInfo.addEventListener(state => {
    setOnline(!!state.isConnected);
  });
});

WebSocket + React Query: במקום לנהל WebSocket state ידנית ב-Zustand, כשמגיע event בWebSocket, קראו ל-queryClient.invalidateQueries(['orders']). React Query יבצע refetch חכם ויעדכן את כל ה-components שsubscribed לquery. זה מפחית bugs ב-~80% לעומת ניהול state WebSocket ידנית ב-Redux.

MMKV, למה לא AsyncStorage

MMKV הוא C++ native module שעושה memory-mapped files. הוא עד 30x מהיר מAsyncStorage. ב-app ישראלי של סטארטאפ שמגיש 50K sessions ביום, ה-session restore מ-MMKV לוקח 3-5ms לעומת 80-150ms מAsyncStorage. ההבדל מורגש בפתיחת האפליקציה.

MMKV גם synchronous, אין צורך ב-async/await לread operations. זה מפשט קוד רב, בעיקר ב-store initializers וב-hooks שנקראים ב-render.

טעויות נפוצות

Zustand store עם fetch ב-useEffect
TanStack Query עם useQuery
React Context עם useState
Redux RTK Query
שגיאה TypeScript בcompile time
Warning בconsole
Component נכנס ל-infinite render loop
הכל עובד כרגיל, זו התנהגות תקינה
MMKV כותב ל-memory בלבד, AsyncStorage לdisk
AsyncStorage תומך בטקסט, MMKV רק בnumbers
MMKV הוא C++ native עם synchronous API, מהיר עד 30x מ-AsyncStorage
AsyncStorage מהיר יותר כי הוא built-in

סיכום, ארכיטקטורת state ב-2026

רוצה ללמוד עם מעקב התקדמות, קוויזים ותעודה?

כל 130 השיעורים פתוחים בחינם, כולל נגן אינטראקטיבי, שמירת התקדמות ותעודה דיגיטלית בסיום.