יְלָדִים
Children מאפשר לך לתמרן ולשנות את ה-JSX שקיבלת בתור children אבזר.
const mappedChildren = Children.map(children, child =>
<div className="Row">
{child}
</div>
);הפניה
Children.count(children)
התקשר ל-Children.count(children) כדי לספור את מספר הילדים במבנה הנתונים children.
import { Children } from 'react';
function RowList({ children }) {
return (
<>
<h1>Total rows: {Children.count(children)}</h1>
...
</>
);
}פרמטרים
children: הערך שלchildrenprop שהתקבל על ידי הרכיב שלך.
מחזירה
מספר הצמתים בתוך children אלה.
אזהרות
- צמתים ריקים (
null,undefinedובוליאנים), מחרוזות, מספרים ו-React אלמנטים נחשבים כצמתים בודדים. מערכים לא נחשבים כצמתים בודדים, אבל הילדים שלהם כן. המעבר אינו מעמיק יותר מאלמנטים של React: הם לא עוברים רינדור, ולא עוברים על הילדים שלהם. Fragments לא עוברים.
Children.forEach(children, fn, thisArg?)
התקשר ל-Children.forEach(children, fn, thisArg?) כדי להפעיל קוד עבור כל ילד במבנה הנתונים children.
import { Children } from 'react';
function SeparatorList({ children }) {
const result = [];
Children.forEach(children, (child, index) => {
result.push(child);
result.push(<hr key={index} />);
});
// ...פרמטרים
children: הערך שלchildrenprop שהתקבל על ידי הרכיב שלך.fn: הפונקציה שברצונך להפעיל עבור כל ילד, בדומה לשיטת מערךforEachcallback. היא תיקרא עם הילד כארגומנט הראשון והאינדקס שלו כארגומנט השני. האינדקס מתחיל ב-0ומתרחב בכל קריאה.- אופציונלי
thisArg: הערךthisשבאמצעותו יש לקרוא לפונקציהfn. אם מושמט, זהundefined.
מחזירה
Children.forEach מחזירה undefined.
אזהרות
- צמתים ריקים (
null,undefinedובוליאנים), מחרוזות, מספרים ו-React אלמנטים נחשבים כצמתים בודדים. מערכים לא נחשבים כצמתים בודדים, אבל הילדים שלהם כן. המעבר אינו מעמיק יותר מאלמנטים של React: הם לא עוברים רינדור, ולא עוברים על הילדים שלהם. Fragments לא עוברים.
Children.map(children, fn, thisArg?)
התקשר ל-Children.map(children, fn, thisArg?) כדי למפות או לשנות כל ילד במבנה הנתונים children.
import { Children } from 'react';
function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}פרמטרים
children: הערך שלchildrenprop שהתקבל על ידי הרכיב שלך.fn: פונקציית המיפוי, בדומה לשיטת מערךmapהתקשרות חוזרת. היא תיקרא עם הילד כארגומנט הראשון והאינדקס שלו כארגומנט השני. האינדקס מתחיל ב-0ומתרחב בכל קריאה. אתה צריך להחזיר https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) התקשרות ריקה מפונקציה זו, no___.undefined, או בוליאני), מחרוזת, מספר, אלמנט React או מערך של צמתים React אחרים.- אופציונלי
thisArg: הערךthisשבאמצעותו יש לקרוא לפונקציהfn. אם מושמט, זהundefined.
מחזירה
אם children הוא null או undefined, מחזיר את אותו הערך.
אחרת, מחזיר מערך שטוח המורכב מהצמתים שהחזרת מהפונקציה fn. המערך המוחזר יכיל את כל הצמתים שהחזרת מלבד null ו-undefined.
אזהרות
-
צמתים ריקים (
null,undefinedובוליאנים), מחרוזות, מספרים ו-React אלמנטים נחשבים כצמתים בודדים. מערכים לא נחשבים כצמתים בודדים, אבל הילדים שלהם כן. המעבר אינו מעמיק יותר מאלמנטים של React: הם לא עוברים רינדור, ולא עוברים על הילדים שלהם. Fragments לא עוברים. -
אם אתה מחזיר אלמנט או מערך של אלמנטים עם מפתחות מ-
fn, מפתחות האלמנטים המוחזרים ישולבו אוטומטית עם המפתח של הפריט המקורי המתאים מ-children. כאשר אתה מחזיר אלמנטים מרובים מ-fnבמערך, המפתחות שלהם צריכים להיות ייחודיים רק זה בזה מקומית.
Children.only(children)
התקשר ל-Children.only(children) כדי לקבוע ש-children מייצג אלמנט React בודד.
function Box({ children }) {
const element = Children.only(children);
// ...פרמטרים
children: הערך שלchildrenprop שהתקבל על ידי הרכיב שלך.
מחזירה
אם children הוא אלמנט חוקי, מחזיר את הרכיב הזה.
אחרת, זורק שגיאה.
אזהרות
- השיטה הזו תמיד זורקת אם אתה מעביר מערך (כגון ערך ההחזרה של
Children.map) בתורchildren. במילים אחרות, היא אוכפת שchildrenהוא אלמנט React בודד, לא שזה מערך עם אלמנט בודד.
Children.toArray(children)
התקשר ל-Children.toArray(children) כדי ליצור מערך מתוך מבנה הנתונים children.
import { Children } from 'react';
export default function ReversedList({ children }) {
const result = Children.toArray(children);
result.reverse();
// ...פרמטרים
children: הערך שלchildrenprop שהתקבל על ידי הרכיב שלך.
מחזירה
מחזירה מערך שטוח של אלמנטים ב-children.
אזהרות
- צמתים ריקים (
null,undefinedובוליאנים) יושמטו במערך המוחזר. מפתחות האלמנטים המוחזרים יחושבו לפי מפתחות האלמנטים המקוריים ורמת הקינון והמיקום שלהם. זה מבטיח שהשטחת המערך לא תחולל שינויים בהתנהגות.
שימוש
ילדים משתנים
כדי להפוך את הילדים JSX שהרכיב שלך מקבל כאביזר children, קרא Children.map:
import { Children } from 'react';
function RowList({ children }) {
return (
<div className="RowList">
{Children.map(children, child =>
<div className="Row">
{child}
</div>
)}
</div>
);
}בדוגמה שלמעלה, ה-RowList עוטף כל ילד שהוא מקבל לתוך מיכל <div className="Row">. לדוגמה, נניח שרכיב האב מעביר שלושה תגיות <p> בתור התמיכה children ל-RowList:
<RowList>
<p>This is the first item.</p>
<p>This is the second item.</p>
<p>This is the third item.</p>
</RowList>לאחר מכן, עם היישום RowList לעיל, התוצאה הסופית המעובדת תיראה כך:
<div className="RowList">
<div className="Row">
<p>This is the first item.</p>
</div>
<div className="Row">
<p>This is the second item.</p>
</div>
<div className="Row">
<p>This is the third item.</p>
</div>
</div>Children.map דומה ל-להמרת מערכים עם map(). ההבדל הוא שמבנה הנתונים children נחשב לאטום. זה אומר שגם אם זה לפעמים מערך, לא צריך להניח שזה מערך או כל סוג נתונים מסוים אחר. זו הסיבה שאתה צריך use Children.map אם אתה צריך לשנות את זה.
import { Children } from 'react'; export default function RowList({ children }) { return ( <div className="RowList"> {Children.map(children, child => <div className="Row"> {child} </div> )} </div> ); }
Deep Dive
ב-React, אבזר children נחשב למבנה נתונים אטום. זה אומר שאתה לא צריך להסתמך על איך זה בנוי. כדי להפוך, לסנן או לספור ילדים, עליך use את שיטות Children.
בפועל, מבנה הנתונים children מיוצג לעתים קרובות כמערך פנימי. עם זאת, אם יש רק ילד בודד, אז React לא יצור מערך נוסף מכיוון שזה יוביל לתקורת memory מיותרת. כל עוד אתה use את שיטות Children במקום לבחון ישירות את הפרופס של children, הקוד שלך לא ישבר גם אם React ישנה את האופן שבו מבנה הנתונים מיושם בפועל.
גם כאשר children הוא מערך, ל-Children.map יש use התנהגות מיוחדת מלאה. לדוגמה, Children.map משלב את ה-keys ברכיבים המוחזרים עם המפתחות ב-children שהעברת אליו. זה מבטיח שילדי JSX המקוריים לא “יאבדו” מפתחות גם אם הם עטופים כמו בדוגמה למעלה.
הפעלת קוד עבור כל ילד
התקשר ל-Children.forEach כדי לחזור על כל ילד במבנה הנתונים children. זה לא מחזיר שום ערך והוא דומה לשיטת מערך forEach. אתה יכול use להפעיל לוגיקה מותאמת אישית כמו בניית מערך משלך.
import { Children } from 'react'; export default function SeparatorList({ children }) { const result = []; Children.forEach(children, (child, index) => { result.push(child); result.push(<hr key={index} />); }); result.pop(); // Remove the last separator return result; }
import { Children } from 'react'; export default function RowList({ children }) { return ( <div className="RowList"> <h1 className="RowListHeader"> Total rows: {Children.count(children)} </h1> {Children.map(children, child => <div className="Row"> {child} </div> )} </div> ); }
המרת ילדים למערך
התקשר ל-Children.toArray(children) כדי להפוך את מבנה הנתונים children למערך JavaScript רגיל. זה מאפשר לך לתפעל את המערך עם שיטות מערך מובנות כמו filter, sort, או reverse.
import { Children } from 'react'; export default function ReversedList({ children }) { const result = Children.toArray(children); result.reverse(); return result; }
חלופות
חשיפת רכיבים מרובים
מניפולציה של ילדים בשיטות Children מובילה לרוב לקוד שביר. כאשר אתה מעביר ילדים לרכיב ב-JSX, אתה בדרך כלל לא מצפה שהרכיב יבצע מניפולציה או שינוי של הילדים הבודדים.
כאשר אתה יכול, נסה להימנע משימוש בשיטות Children. לדוגמה, אם אתה רוצה שכל ילד של RowList יהיה עטוף ב-<div className="Row">, ייצא רכיב Row, ועטוף ידנית כל שורה לתוכו כך:
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList> <Row> <p>This is the first item.</p> </Row> <Row> <p>This is the second item.</p> </Row> <Row> <p>This is the third item.</p> </Row> </RowList> ); }
בניגוד לשימוש ב-Children.map, גישה זו אינה עוטפת כל ילד באופן אוטומטי. עם זאת, לגישה זו יש יתרון משמעותי בהשוואה לדוגמה הקודמת עם Children.map מכיוון שuse היא עובדת גם אם אתה ממשיך לחלץ עוד רכיבים. לדוגמה, זה עדיין עובד אם אתה מחלץ את רכיב ה-MoreRows משלך:
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList> <Row> <p>This is the first item.</p> </Row> <MoreRows /> </RowList> ); } function MoreRows() { return ( <> <Row> <p>This is the second item.</p> </Row> <Row> <p>This is the third item.</p> </Row> </> ); }
זה לא יעבוד עם Children.map כי use הוא “יראה” את <MoreRows /> כילד יחיד (ושורה בודדת).
קבלת מערך של אובייקטים כאביזר
אתה יכול גם להעביר מערך באופן מפורש בתור אביזר. לדוגמה, RowList זה מקבל מערך rows בתור אבזר:
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList rows={[ { id: 'first', content: <p>This is the first item.</p> }, { id: 'second', content: <p>This is the second item.</p> }, { id: 'third', content: <p>This is the third item.</p> } ]} /> ); }
מכיוון שrows הוא מערך JavaScript רגיל, הרכיב RowList יכול use שיטות מערך מובנות כמו map עליו.
דפוס זה useמלא במיוחד כאשר אתה רוצה להיות מסוגל להעביר מידע נוסף כנתונים מובנים יחד עם ילדים. בדוגמה שלהלן, הרכיב TabSwitcher מקבל מערך של אובייקטים בתור tabs אבזר:
import TabSwitcher from './TabSwitcher.js'; export default function App() { return ( <TabSwitcher tabs={[ { id: 'first', header: 'First', content: <p>This is the first item.</p> }, { id: 'second', header: 'Second', content: <p>This is the second item.</p> }, { id: 'third', header: 'Third', content: <p>This is the third item.</p> } ]} /> ); }
בניגוד להעברת הילדים בתור JSX, גישה זו מאפשרת לך לשייך כמה נתונים נוספים כמו header לכל פריט. Because אתה עובד עם tabs ישירות, וזה מערך, אתה לא צריך את שיטות Children.
קריאה לאביזר עיבוד כדי להתאים אישית את העיבוד
במקום לייצר JSX עבור כל פריט בודד, אתה יכול גם להעביר פונקציה שמחזירה JSX, ולקרוא לפונקציה הזו בעת הצורך. בדוגמה זו, הרכיב App מעביר פונקציה renderContent לרכיב TabSwitcher. הרכיב TabSwitcher קורא ל-renderContent רק עבור הכרטיסייה שנבחרה:
import TabSwitcher from './TabSwitcher.js'; export default function App() { return ( <TabSwitcher tabIds={['first', 'second', 'third']} getHeader={tabId => { return tabId[0].toUpperCase() + tabId.slice(1); }} renderContent={tabId => { return <p>This is the {tabId} item.</p>; }} /> ); }
אבזר כמו renderContent נקרא render prop because זהו אבזר שמציין כיצד לרנדר חלק מהממשק user. עם זאת, אין בזה שום דבר מיוחד: זה אביזר רגיל שהוא במקרה פונקציה.
עיבוד props הן פונקציות, כך שתוכל להעביר אליהן מידע. לדוגמה, רכיב RowList זה מעביר את ה-id וה-index של כל שורה ל-renderRow עיבוד אבזר, אשר uses index כדי להדגיש שורות זוגיות:
import { RowList, Row } from './RowList.js'; export default function App() { return ( <RowList rowIds={['first', 'second', 'third']} renderRow={(id, index) => { return ( <Row isHighlighted={index % 2 === 0}> <p>This is the {id} item.</p> </Row> ); }} /> ); }
זוהי דוגמה נוספת לאופן שבו מרכיבי הורה וילד יכולים לשתף פעולה מבלי לתמרן את הילדים.
פתרון בעיות
אני מעביר רכיב מותאם אישית, אבל שיטות Children לא מציגות את תוצאת העיבוד שלו
נניח שאתה מעביר שני ילדים ל-RowList כך:
<RowList>
<p>First item</p>
<MoreRows />
</RowList>אם תעשה Children.count(children) בתוך RowList, תקבל 2. גם אם MoreRows מעבד 10 פריטים שונים, או אם הוא מחזיר null, Children.count(children) עדיין יהיה 2. מנקודת המבט של ה-RowList, הוא “רואה” רק את ה-JSX שהוא קיבל. הוא לא “רואה” את החלק הפנימי של רכיב MoreRows.
המגבלה מקשה על חילוץ רכיב. זו הסיבה שהעדיפות חלופות על פני שימוש ב-Children.