רינדור מותנה ב-React פירושו לבחור מה קומפוננטה מחזירה בעזרת JavaScript רגיל: משפט if לפני ה-return, אופרטור טרנרי cond ? a : b בתוך ה-JSX, או cond && <Thing /> כדי להציג משהו או כלום. אין תחביר תבניות מיוחד ללמוד.
לחצו על הכפתור וגם ההודעה וגם התווית מתחלפות, כי כל טרנרי בוחר מחרוזת אחרת ברינדור הבא. שנו את useState(false) ל-useState(true) והתצוגה המקדימה מתחילה במצב מחובר.
משפטי if ו-return מוקדם
סוגריים מסולסלים ב-JSX מקבלים ביטויים, לא משפטים, ולכן אי אפשר לכתוב {if (x) ...} בתוך ה-markup. אפשר לכתוב כל if שרוצים לפני ה-return. כשמצב שלם של הקומפוננטה נראה אחרת (טעינה, שגיאה, ריק), החזירו מוקדם עבור כל מקרה ושמרו את ה-markup הראשי למקרה הרגיל.
כל כפתור מעביר את תיבת הדואר למצב אחר, ולכל מצב יש ענף קצר וקריא משלו. החזרות מוקדמות שומרות את המסלול הרגיל בתחתית בלי קינון.
טרנרי בתוך JSX
כשרק חלק קטן מה-markup משתנה, טרנרי inline שומר את הכל ביחד: {isOpen ? <CloseIcon /> : <MenuIcon />}. טרנרי עובד טוב לשתי אפשרויות. טרנרי מקונן לשלוש אפשרויות או יותר נהיה קשה לקריאה מהר מאוד:
// Hard to follow
{status === 'loading' ? <Spinner /> : status === 'error' ? <ErrorBox /> : <List />}
כשרואים דבר כזה, העבירו את הלוגיקה לשרשרת if לפני ה-return (כמו למעלה) או למפת אובייקט (בהמשך).
&& וה-0 המיותר
cond && <Thing /> מרנדר את <Thing /> כש-cond הוא truthy וכלום כשהוא false. זה עובד כי && מחזיר את הצד הראשון שלו כשהצד הזה falsy, ו-React לא מרנדרת כלום עבור false, null ו-undefined.
המלכודת היא שגם 0 הוא falsy, ו-React כן מרנדרת מספרים. כך ש-count && <Badge /> עם ספירה של 0 שם 0 מילולי בדף.
כשאין הודעות שלא נקראו, בשורה הראשונה כתוב "Broken: 0" ואילו השנייה לא מציגה כלום אחרי "Fixed:". לחצו על New message ושתיהן מסכימות; לחצו על Mark all read וה-0 חוזר. הפתרון הוא לתת ל-&& בוליאני אמיתי: unread > 0, items.length > 0 או Boolean(unread). למחרוזת ריקה '' יש אותה בעיה עקרונית, אבל היא לא מרונדרת כמשהו גלוי, אז בדרך כלל לא שמים לב.
החזרת null
קומפוננטה יכולה להחזיר null כדי לא לרנדר כלום. באנר אזהרה שמופיע רק כשיש ממה להזהיר הוא דוגמה טובה: ההורה תמיד מרנדר <Warning /> והקומפוננטה מחליטה.
שימו לב לקונסול: הפונקציה Warning רושמת בכל רינדור, כולל אלה שבהם היא מחזירה null. החזרת null מדלגת על הפלט, לא על העבודה. ההוקים והאפקטים שלה עדיין רצים, כך שאם קומפוננטה כבדה או מתחילה משהו (טיימר, מנוי), החליטו בהורה ואל תרנדרו אותה בכלל.
שמירת JSX במשתנה
JSX הוא ערך, ולכן אפשר להציב אותו במשתנה, לבנות אותו עם משפטי if ולהכניס אותו ל-markup אחר כך. זה עוזר כשחלק אחד מ-layout גדול יותר תלוי בכמה תנאים.
function Checkout({ cart, user }) {
let action;
if (!user) {
action = <a href="/login">Log in to check out</a>;
} else if (cart.length === 0) {
action = <p>Your cart is empty.</p>;
} else {
action = <button>Pay now</button>;
}
return (
<section>
<h2>Your cart</h2>
{action}
</section>
);
}
בחירה בין כמה קומפוננטות עם אובייקט
כשהבחירה היא "אחת מתוך הרבה", אובייקט שממפה כל ערך לקומפוננטה קצר יותר משרשרת if ארוכה או switch, והוספת מקרה היא שורה אחת.
TABS[tab] בוחר קומפוננטה, ו-<Panel /> מרנדר אותה. המשתנה חייב להתחיל באות גדולה: JSX מתייחס ל-<panel /> כתגית HTML. הוסיפו ל-TABS רשומת notifications עם קומפוננטה משלה, ויופיע כפתור רביעי.
הסתרה היא לא כמו אי רינדור
יש שתי דרכים להעלים משהו, והן מתנהגות אחרת. show && <Counter /> מסיר את הקומפוננטה מהעץ כש-show הוא false. React עושה לה unmount וזורקת את ה-state שלה, כך שהיא מתחילה מאפס כשהיא חוזרת. הסתרה עם CSS (style={{ display: show ? 'block' : 'none' }}) משאירה אותה mounted, ולכן ה-state שלה שורד.
לחצו כמה פעמים על שני המונים, בטלו את הסימון בתיבה, ואז סמנו אותה שוב. המונה הראשון חזר ל-0 והשני עדיין מחזיק את המספר שלו. אף אחד מהם לא שגוי: הסרה נכונה לטופס שצריך להתחיל ריק בכל פעם, הסתרה נכונה לטאבים שמיקום הגלילה או הקלט שלהם צריכים להישמר. הדף על רינדור מסביר איך React מחליטה שקומפוננטה היא אותה אחת בין רינדורים.
React 19.2 מוסיפה אפשרות שלישית, <Activity mode="hidden">, שמסתירה את הילדים שלה ושומרת את ה-state שלהם כמו CSS, וגם מנקה את האפקטים שלהם עד שהם מוצגים שוב.
props ומאפיינים מותנים
תנאים הם לא רק השאלה אילו אלמנטים מופיעים. כל prop יכול לקבל ערך מחושב, ולכן טרנרי או השוואה בתוך מאפיין הם לעיתים קרובות כל מה שצריך:
<button disabled={!canSave} className={hasError ? 'field error' : 'field'}>
Save
</button>
<input aria-invalid={hasError} placeholder={user ? user.email : 'you@example.com'} />
כשהערך של prop הוא undefined, React משמיטה את המאפיין לגמרי, וזו דרך נקייה להוסיף מאפיין רק לפעמים: title={isTruncated ? fullText : undefined}. למאפיינים בוליאניים כמו disabled ו-checked, העבירו true או false ו-React מוסיפה או מסירה אותם.
תנאים ברשימות
כדי להשמיט פריטים מרשימה, סננו את המערך לפני ה-map במקום להחזיר null מתוך map. items.filter((i) => i.done).map(...) נקרא בדיוק כמו מה שהוא עושה, והדף על רשימות ו-keys מראה את זה בדוגמה רצה.
שאלות נפוצות
איך כותבים משפט if בתוך JSX?
אי אפשר לשים משפט if בתוך סוגריים מסולסלים של JSX, כי הם מקבלים ביטויים. השתמשו באופרטור טרנרי (cond ? a : b) או ב-&& בתוך ה-JSX, או הריצו את ה-if לפני ה-return ושמרו את ה-JSX במשתנה.
למה הקומפוננטה שלי מציגה 0?
count && <Badge /> מחזיר את count עצמו כשהוא 0, ו-React מרנדרת את המספר 0. הפכו את הצד הראשון לבוליאני אמיתי: count > 0 && <Badge />.
איך לא מרנדרים כלום ב-React?
החזירו null מהקומפוננטה, או שימו null, undefined, false או true ב-JSX. React לא מרנדרת אף אחד מהם.
קומפוננטה שמחזירה null עדיין רצה?
כן. הפונקציה שלה רצה, ההוקים שלה רצים והאפקטים שלה מופעלים. החזרת null רק אומרת שהיא לא מוסיפה כלום לדף. כדי לדלג על העבודה לגמרי, אל תרנדרו את הקומפוננטה מההורה.
להסתיר קומפוננטה עם CSS או להסיר אותה?
הסרה (show && <Panel />) עושה לה unmount, כך שה-state שלה נזרק ומתחיל מחדש בפעם הבאה. הסתרה עם display: none משאירה אותה mounted ושומרת את ה-state. בחרו לפי השאלה אם ה-state צריך לשרוד.