Fragment ב-React מאפשר לקומפוננטה להחזיר כמה אלמנטים בלי לעטוף אותם ב-<div> נוסף. כתבו <> לפני האלמנט הראשון ו-</> אחרי האחרון, ו-React מכניסה את האלמנטים ישר לתוך ההורה, בלי צומת משלה בדף.
כל Greeting מחזירה כותרת ופסקה זו לצד זו. מחקו את השורות <> ו-</> והתצוגה המקדימה תציג במקום זאת שגיאת build, כי הפונקציה מנסה עכשיו להחזיר שני ערכים.
למה קומפוננטה צריכה שורש אחד
JSX הוא דרך נעימה יותר לכתוב קריאות לפונקציות. <h2>Hi</h2> מתקמפל בערך ל-jsx('h2', { children: 'Hi' }), ביטוי אחד. משפט return יכול להחזיר ערך אחד, ולכן שתי תגיות אחיות בלי כלום מסביבן הן כמו לכתוב return a b: הקומפיילר עוצר עם "Adjacent JSX elements must be wrapped in an enclosing tag".
לפני שהיו fragments, הפתרון היה <div> עוטף. זה עובד, אבל ה-div הוא אלמנט אמיתי. הוא מופיע ב-DOM, משתתף ב-layout של CSS, וחלק מההורים לא מקבלים אותו כילד בכלל. fragment מקיים את כלל השורש האחד ואז נעלם.
אפשר לראות שהוא לא מוסיף כלום. הדוגמה שמתחת מרנדרת את אותם שני פריטים דרך fragment ודרך div, ואז סופרת את הילדים הישירים של כל מכל.
הקונסול מדפיס 2 עבור ה-fragment, שני ה-spans, ו-1 עבור ה-div, שעוטף אותם. במכל flex או grid ההבדל הזה קובע אם ה-spans מסודרים כפריטים נפרדים או נארזים לפריט אחד.
התחביר הקצר והקומפוננטה Fragment
יש שתי דרכים לכתוב fragment, ושתיהן מתקמפלות לאותו דבר:
import { Fragment } from 'react';
// Short syntax, no import needed
<>
<dt>React</dt>
<dd>A library for building user interfaces</dd>
</>
// Long form
<Fragment>
<dt>React</dt>
<dd>A library for building user interfaces</dd>
</Fragment>
השתמשו ב-<>...</> כמעט בכל מקום. פנו ל-<Fragment> במקרה אחד: כשה-fragment צריך key. התחביר הקצר לא יכול לקבל מאפיינים, ולכן <key={id}> הוא לא JSX תקין. אולי תראו גם <React.Fragment> בקוד ישן; זו אותה קומפוננטה, דרך ה-import של React כברירת מחדל.
fragments עם key ברשימות
כשמרנדרים רשימה עם map, כל פריט צריך key כדי ש-React תוכל לעקוב אחריו בין רינדורים (הדף על רשימות ו-keys מסביר למה). אם כל פריט הוא קבוצה של אלמנטים ולא אלמנט אחד, ה-key הולך על ה-fragment, ולכן צריך את הצורה הארוכה.
רשימת הגדרות היא המקרה הקלאסי: כל רשומה היא <dt> ו-<dd>, ו-<dl> צריך להכיל אותם ישירות.
לחצו על Reverse והזוגות מחליפים מקומות כיחידות שלמות, כי לכל זוג יש key משלו. גם <div> שעוטף כל זוג היה יכול לקבל key, והוא HTML תקין בתוך dl, אבל הוא מוסיף ל-markup רמה שלישית ש-fragment חוסך.
fragments בתוך טבלאות
בטבלאות עטיפה מיותרת כואבת הכי הרבה. <tr> מצפה לתאי <td> ו-<th> כילדים ישירים. אם קומפוננטה שמחזירה כמה תאים עוטפת אותם ב-<div>, הדפדפן מתייחס ל-div כאל תא מוזר אחד, והתאים שבתוכו מפסיקים להתיישר עם הכותרת.
בשורת ה-fragment המחיר נמצא מתחת ל-Price והמלאי מתחת ל-Stock. בשורת ה-div שני התאים דחוסים לעמודת Price ועמודת Stock ריקה. בפיתוח React גם רושמת אזהרה ש-<div> לא יכול להיות ילד של <tr>; התצוגה המקדימה רצה כמו build של פרודקשן, אז היא מדלגת על הבדיקה הזו, ומה שרואים כאן הוא ה-layout השבור. עם רינדור בצד השרת אותה טעות יכולה לגרום גם לשגיאות hydration, כי ה-parser של ה-HTML בדפדפן מזיז את ה-div שלא במקומו.
אותו כלל חל על <ul> ו-<ol>, שצריכים להכיל אלמנטי <li>, ועל <select>, שצריך להכיל אלמנטי <option>. בכל פעם שקומפוננטה מחזירה חלקים ששייכים ישירות לתוך הורה כזה, החזירו fragment.
fragments או מערכים
קומפוננטה יכולה גם להחזיר מערך של אלמנטים, ו-React מרנדרת אותם לפי הסדר כמו fragment. כל אלמנט במערך מוחזר צריך key משלו, בדיוק כמו הפלט של map, כי React מתייחסת למערך כרשימה:
function Cells() {
return [<td key="price">$4</td>, <td key="stock">In stock</td>];
}
זה עובד, אבל זה רועש יותר: פסיקים בין אלמנטים, key על כל אחד, וסוגר מרובע שקל לפספס כשה-markup גדל. השתמשו ב-fragment לקבוצה קבועה של אלמנטים, ושמרו מערכים למה שהם באופן טבעי, התוצאה של מעבר על נתונים.
מה fragment לא יכול לעשות
מכיוון ש-fragment לא מרנדר אלמנט, אין לו איפה לשים class, סגנון, event handler או ref. <Fragment className="row"> לא עושה שום דבר מועיל, וה-prop היחיד ש-React קוראת מ-fragment הוא key. כשצריך לעצב את הקבוצה או להאזין ללחיצות עליה, צריך בכל זאת אלמנט אמיתי, וזה בסדר: בחרו אחד שתקין במקום שבו הוא נמצא, כמו <tbody> לקיבוץ שורות או <li> שמחזיק פריט ברשימה.
fragment עובד בכל מקום שבו אלמנט עובד, כולל בתוך רינדור מותנה: {show && <>...</>} מציג או מסתיר את כל הקבוצה בבת אחת.
שאלות נפוצות
מה זה Fragment ב-React?
Fragment הוא עטיפה שמאפשרת לקומפוננטה להחזיר כמה אלמנטים כערך אחד בלי להוסיף צומת DOM. <><h1 /><p /></> מכניס את ה-h1 ואת ה-p ישירות לתוך ההורה.
מה ההבדל בין <> ל-<Fragment>?
הם מייצרים אותו דבר. <>...</> הוא התחביר הקצר. <Fragment> היא הצורה הארוכה שמייבאים מ-react, והיא היחידה שיכולה לקבל key, מה שצריך כשמרנדרים fragments ברשימה.
למה להשתמש ב-Fragment במקום div?
div הוא אלמנט אמיתי. הוא יכול לשבור layouts שמצפים לילדים ישירים (מכלי flex ו-grid, ul, table, tr) והוא מוסיף רעש ל-DOM. Fragment לא מוסיף כלום.
האם ל-Fragment יכול להיות className או onClick?
לא. Fragment לא מרנדר שום אלמנט, ולכן אין על מה לשים class, סגנון או event handler. ה-prop היחיד שהוא מקבל הוא key. אם צריך אחד מאלה, השתמשו באלמנט אמיתי.
למה אני מקבל "Adjacent JSX elements must be wrapped in an enclosing tag"?
קומפוננטה החזירה שני אלמנטים אחים בלי שום דבר מסביבם. JSX מתקמפל לביטוי יחיד, ולכן עטפו את האחים ב-<>...</>.