לדלג לתוכן

הפרויקט שכל מעסיק מזהה מיד - חנות עם עגלת קניות ב-React

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

מבנה הבסיס - רשימת מוצרים

מתחילים מדף שמציג רשימת מוצרים בגריד - כל מוצר עם תמונה, שם, מחיר וכפתור "הוסף לעגלה". זה חלק קל יחסית, בעיקר תרגול של רינדור רשימות עם map ומעבר props. האתגר האמיתי מתחיל ברגע שלוחצים על הכפתור הזה - מה קורה לנתונים אחרי הלחיצה, ואיך מסך אחר לגמרי, הכרטיסייה של העגלה, יודע שהמוצר נוסף.

Context - הפתרון לבעיה של props מכל הכיוונים

אם תנסו להעביר את מצב העגלה כ-props ממש רחוק - מרשימת המוצרים, דרך שכבת ניווט, עד לאייקון העגלה בפינת המסך - תגלו מהר מאוד למה זה מכונה "prop drilling", ולמה זה כאב ראש. הפתרון הנקי הוא React Context - מקום מרכזי שמחזיק את מצב העגלה, ושכל component שצריך אותו יכול "להתחבר" אליו ישירות, בלי להעביר אותו ידנית דרך כל שכבת ביניים.

const CartContext = createContext();

function CartProvider({ children }) {
  const [items, setItems] = useState([]);
  return (
    <CartContext.Provider value={{ items, setItems }}>
      {children}
    </CartContext.Provider>
  );
}

זה בדיוק המושג שממחיש למה "הרמת state" (lifting state up) לא תמיד מספיקה - כשהאפליקציה גדלה, צריך פתרון שמתאים לכל העץ, לא רק להורה הקרוב ביותר. אם המושג הזה עדיין לא ברור לכם עד הסוף, שווה לעבור על ההסברים המסודרים בקורס צד לקוח שלנו לפני שאתם מתחילים לכתוב את החנות.

הוספה, הסרה, וכמויות

עגלת קניות אמיתית היא לא רק "הוסף מוצר" - היא צריכה לזהות שאם המוצר כבר בעגלה, מגדילים את הכמות שלו במקום ליצור שורה כפולה. זה דורש לוגיקה של חיפוש בתוך מערך, ועדכון תנאי - אם המוצר קיים, מעדכנים כמות, אחרת מוסיפים אותו. אותה לוגיקה, בכיוון ההפוך, נדרשת גם להסרה ולהקטנת כמות.

function addToCart(product) {
  setItems(prev => {
    const existing = prev.find(item => item.id === product.id);
    if (existing) {
      return prev.map(item =>
        item.id === product.id ? { ...item, qty: item.qty + 1 } : item
      );
    }
    return [...prev, { ...product, qty: 1 }];
  });
}

חישוב סכומים

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

מה זה מוכיח למעסיק

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

המלכודות הנפוצות

טעות ראשונה - עדכון מערך ה-state בצורה ישירה במקום ליצור עותק חדש, מה שמונע מ-React לזהות שינוי. טעות שנייה - לשכוח מצב שבו העגלה ריקה, ולהציג מסך שבור במקום הודעה ברורה. טעות שלישית - לחשב מחדש דברים כבדים בכל רינדור בלי useMemo, מה שמאט חנויות עם הרבה מוצרים.

מה מקבלים בסוף

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

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

הצטרפו לקהילה בדיסקורד