לדלג לתוכן

7.6 דפוסי תכנון וביצועים תרגול

תרגול - דפוסי תכנון וביצועים - Design Patterns and Performance


תרגיל 1 - Compound Components - Select מותאם

צרו קומפוננטת Select מותאמת אישית בדפוס Compound Components.

דרישות:
- קומפוננטות: Select, Select.Trigger, Select.Options, Select.Option
- שיתוף state דרך context (ערך נבחר, פתוח/סגור)
- תמיכה ב-placeholder, disabled, ו-onChange callback
- סגירה על לחיצה מחוץ לקומפוננטה
- תמיכה בניווט מקלדת (חיצים, Enter, Escape)


תרגיל 2 - HOC מתקדם

צרו שני HOC-ים שימושיים.

דרישות:
- withLogger - HOC שמדפיס ל-console כל רנדר של הקומפוננטה, כולל שם הקומפוננטה, props, וזמן הרנדר
- withPermission - HOC שמציג את הקומפוננטה רק אם למשתמש יש הרשאה מתאימה, אחרת מציג הודעה
- הדגימו שימוש בשני ה-HOC-ים על אותה קומפוננטה (הרכבה)


תרגיל 3 - Render Props - Data Fetcher

צרו קומפוננטת DataFetcher שמשתמשת ב-Render Props.

דרישות:
- מקבלת URL ו-render prop
- מנהלת מצבים: loading, error, data
- תמיכה ב-refetch
- תמיכה ב-polling (שליפה חוזרת כל X שניות)
- הדגימו שימוש עם שתי קומפוננטות שונות שמציגות את הנתונים בדרכים שונות


תרגיל 4 - אופטימיזציית ביצועים

קבלו את הקומפוננטה הבאה שיש בה בעיות ביצועים. תקנו אותה.

function ProductDashboard() {
  const [products, setProducts] = useState(generateProducts(1000));
  const [search, setSearch] = useState("");
  const [sortBy, setSortBy] = useState("name");
  const [selectedId, setSelectedId] = useState<number | null>(null);
  const [theme, setTheme] = useState("light");

  const filteredProducts = products
    .filter((p) => p.name.toLowerCase().includes(search.toLowerCase()))
    .sort((a, b) => (a[sortBy] > b[sortBy] ? 1 : -1));

  const stats = {
    total: filteredProducts.length,
    avgPrice: filteredProducts.reduce((sum, p) => sum + p.price, 0) / filteredProducts.length,
    maxPrice: Math.max(...filteredProducts.map((p) => p.price)),
  };

  return (
    <div className={theme}>
      <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
        החלף ערכת נושא
      </button>
      <input value={search} onChange={(e) => setSearch(e.target.value)} />
      <div>סה"כ: {stats.total}, ממוצע: {stats.avgPrice.toFixed(0)}</div>
      <ul>
        {filteredProducts.map((product) => (
          <ProductItem
            key={product.id}
            product={product}
            isSelected={product.id === selectedId}
            onSelect={() => setSelectedId(product.id)}
            style={{ backgroundColor: theme === "light" ? "#fff" : "#333" }}
          />
        ))}
      </ul>
    </div>
  );
}

function ProductItem({ product, isSelected, onSelect, style }) {
  console.log(`Rendering: ${product.name}`);
  return (
    <li onClick={onSelect} style={{ ...style, fontWeight: isSelected ? "bold" : "normal" }}>
      {product.name} - {product.price} ש"ח
    </li>
  );
}

דרישות:
- זהו את כל בעיות הביצועים
- תקנו כל בעיה עם הסבר
- השתמשו ב-useMemo, useCallback, React.memo ככל שנדרש
- הוכיחו ש-ProductItem מרונדר פחות פעמים אחרי התיקון


תרגיל 5 - Compound Components - Form Builder

צרו מערכת בניית טפסים בדפוס Compound Components.

דרישות:
- קומפוננטות: Form, Form.Field, Form.Input, Form.Select, Form.TextArea, Form.Submit, Form.Error
- ה-Form מנהל את כל ה-state והולידציה דרך context
- כל Field מרגיש "חלק" מהטופס ומדווח על שגיאות
- Form.Submit מושבת כשיש שגיאות ולידציה
- תמיכה בולידציה מותאמת לכל שדה
- הדגימו טופס יצירת משתמש עם שדות מגוונים


תרגיל 6 - רשימה וירטואלית (מתקדם)

ממשו רשימה וירטואלית בסיסית בעצמכם (ללא ספרייה).

דרישות:
- הציגו רשימה עם 10,000 פריטים
- רנדרו רק את הפריטים הנראים על המסך (+ buffer)
- תמכו בגלילה חלקה
- תמכו בפריטים בגבהים שונים
- הציגו מונה של כמה פריטים מרונדרים בפועל
- השוו ביצועים לעומת רשימה רגילה (הדגימו את ההבדל)


שאלות

  1. מה היתרון של Compound Components על פני העברת כל האפשרויות כ-props לקומפוננטה אחת?
  2. מתי Render Props עדיין מתאים יותר מהוק מותאם אישית?
  3. מה ההבדל בין React.memo לבין useMemo? מתי נשתמש בכל אחד?
  4. האם React.memo משווה props באופן עמוק (deep) או רדוד (shallow)?
  5. מה ה-trade-off של שימוש ב-virtualization? מתי לא כדאי להשתמש?