רינדור מותנה

מבוא

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


שיטות לרינדור מותנה

1. אופרטור Ternary ( ? : )

function Greeting({ isLoggedIn }) {
    return (
        <div>
            {isLoggedIn ? (
                <h1>ברוך הבא בחזרה!</h1>
            ) : (
                <h1>אנא התחבר</h1>
            )}
        </div>
    );
}

2. אופרטור && (Logical AND)

מציג תוכן רק אם התנאי true:

function Notifications({ count }) {
    return (
        <div>
            {count > 0 && (
                <span className="badge">{count}</span>
            )}
        </div>
    );
}

// אם count = 0, לא יוצג כלום
// אם count = 5, יוצג <span class="badge">5</span>

⚠️ זהירות למלכודת נפוצה: האופרטור && מחזיר את הצד השמאלי כשהוא "נפילה" (falsy). המספר 0 הוא falsy, אך React דווקא מציגה אותו על המסך. לכן בודקים תמיד תנאי מפורש (למשל count > 0) ולא נשענים על הערך עצמו:

// ❌ בעיה
{count && <span>{count}</span>}  // אם count=0, יוצג "0"

// ✅ פתרון
{count > 0 && <span>{count}</span>}

3. if/else רגיל

function StatusMessage({ status }) {
    let message;

    if (status === 'loading') {
        message = <p>טוען...</p>;
    } else if (status === 'error') {
        message = <p className="error">שגיאה!</p>;
    } else if (status === 'success') {
        message = <p className="success">הצלחה!</p>;
    } else {
        message = <p>לא ידוע</p>;
    }

    return <div>{message}</div>;
}

4. switch/case

function Icon({ type }) {
    switch (type) {
        case 'success':
            return <span>✅</span>;
        case 'error':
            return <span>❌</span>;
        case 'warning':
            return <span>⚠️</span>;
        default:
            return <span>ℹ️</span>;
    }
}

5. אובייקט מיפוי

function StatusIcon({ status }) {
    const icons = {
        success: '✅',
        error: '❌',
        warning: '⚠️',
        info: 'ℹ️'
    };

    return <span>{icons[status] || icons.info}</span>;
}

דוגמאות מעשיות

הצגת Loading

function UserList() {
    const [users, setUsers] = useState([]);
    const [loading, setLoading] = useState(true);

    useEffect(() => {
        fetchUsers().then(data => {
            setUsers(data);
            setLoading(false);
        });
    }, []);

    if (loading) {
        return <div className="spinner">טוען...</div>;
    }

    return (
        <ul>
            {users.map(user => (
                <li key={user.id}>{user.name}</li>
            ))}
        </ul>
    );
}

טיפול בשגיאות

function DataDisplay() {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        fetch('/api/data')
            .then(res => res.json())
            .then(setData)
            .catch(err => setError(err.message))
            .finally(() => setLoading(false));
    }, []);

    if (loading) return <p>⏳ טוען...</p>;
    if (error) return <p>❌ שגיאה: {error}</p>;
    if (!data) return <p>📭 אין נתונים</p>;

    return <pre>{JSON.stringify(data, null, 2)}</pre>;
}

מצב ריק

function TodoList({ todos }) {
    if (todos.length === 0) {
        return (
            <div className="empty-state">
                <p>🎉 אין משימות!</p>
                <p>הוסף משימה חדשה להתחיל</p>
            </div>
        );
    }

    return (
        <ul>
            {todos.map(todo => (
                <li key={todo.id}>{todo.text}</li>
            ))}
        </ul>
    );
}

הסתרת קומפוננטה

החזרת null

function Modal({ isOpen, children }) {
    if (!isOpen) {
        return null; // לא מרנדר כלום
    }

    return (
        <div className="modal-overlay">
            <div className="modal-content">
                {children}
            </div>
        </div>
    );
}

CSS Display

function Toggle() {
    const [visible, setVisible] = useState(true);

    return (
        <div>
            <button onClick={() => setVisible(v => !v)}>
                {visible ? 'הסתר' : 'הצג'}
            </button>
            <div style={{ display: visible ? 'block' : 'none' }}>
                תוכן שאפשר להסתיר
            </div>
        </div>
    );
}

Classes דינמיים

function Button({ isActive, isDisabled, children }) {
    // שיטה 1: Template literal
    const className = `btn ${isActive ? 'active' : ''} ${isDisabled ? 'disabled' : ''}`;

    // שיטה 2: Array + filter + join
    const classNames = [
        'btn',
        isActive && 'active',
        isDisabled && 'disabled'
    ].filter(Boolean).join(' ');

    return (
        <button className={classNames} disabled={isDisabled}>
            {children}
        </button>
    );
}

סיכום

שיטה מתי להשתמש
condition ? a : b שני מצבים אפשריים
condition && element הצגה/הסתרה פשוטה
if/else לוגיקה מורכבת
switch מספר מצבים קבועים
return null הסתרה מלאה

הקודם: ← Hooks
הבא: רשימות →

בדקו את עצמכם

נסו לענות לבד לפני שאתם פותחים את התשובה.

  1. מה הסכנה ב-{items.length && <List />}?

    1. כשהמערך ריק יוצג 0 על המסך
    2. אין סכנה
    3. שגיאה
    4. הרשימה תוצג פעמיים
    הצגת התשובה

    תשובה א. 0 הוא ערך ש-React מציגה. כותבים items.length > 0 && ...

  2. איך מסתירים קומפוננטה לגמרי?

    1. מחזירים null
    2. מחזירים false מ-CSS
    3. מוחקים את הקובץ
    4. מחזירים מחרוזת ריקה
    הצגת התשובה

    תשובה א. קומפוננטה שמחזירה null לא מרנדרת כלום.