קומפוננטות ו-Props

קומפוננטות מאפשרות לכם לפצל את ממשק המשתמש לחתיכות עצמאיות המאפשרות שימוש חוזר, ולחשוב על כל חתיכה בנפרד. דף זה מספק מבוא לרעיון של קומפוננטות. תוכלו למצוא הסבר מפורט של ה-API של קומפוננטות כאן.

מבחינה תפיסתית, קומפוננטות הן כמו פונקציות JavaScript. הן מקבלות קלט שרירותי (נקרא “props”) ומחזירות אלמנטים של React המתארים מה אמור להופיע על המסך.

קומפוננטות מסוגי פונקציות ומחלקות

הדרך הפשוטה ביותר להגדיר קומפוננטה היא לכתוב פונקציית JavaScript:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

פונקציה זו היא קומפוננטת React חוקית משום שהיא מקבלת ארגומנט אובייקט נתונים יחיד מסוג “props” (קיצור של המילה תכונות, properties באנגלית) ומחזירה אלמנט React. אנו מכנים קומפוננטות כאלה “קומפוננטת פונקציה” משום שהם פשוט פונקציות JavaScript.

תוכלו גם להשתמש במחלקה של ES6 כדי להגדיר קומפוננטה:

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

שתי הקומפוננטות מעלה הן זהות מנקודת המבט של React.

למחלקות יש כמה תכונות נוספות שנדון בהן בחלק הבא. עד אז, נשתמש בקומפוננטות פונקציה בשל היותן קצרות יותר.

רינדור של קומפוננטה

עד כה, נתקלנו רק ברכיבי React שמייצגים תגי DOM:

const element = <div />;

עם זאת, אלמנטים יכולים גם לייצג קומפוננטות המוגדרות על ידי המשתמש:

const element = <Welcome name="Sara" />;

כאשר React רואה אלמנט המייצג קומפוננטה שהודגרה על ידי המשתמש, היא מעבירה את המאפיינים שהוגדרו ב-JSX לקומפוננטה זו כאובייקט יחיד. אנו קוראים לאובייקט זה “props”.

For example, this code renders “Hello, Sara” on the page:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

const element = <Welcome name="Sara" />;
ReactDOM.render(
  element,
  document.getElementById('root')
);

נסו זאת ב-CodePen

בואו נסכם מה קורה בדוגמה זו:

  1. אנחנו קוראים ל-ReactDOM.render() עם האלמנט <Welcome name="Sara" />.
  2. React קוראת לקומפוננטת Welcome עם {name: 'Sara'} בתור ה-props.
  3. קומפוננטת Welcome שלנו מחזירה אלמנט <h1>Hello, Sara</h1> בתור התוצאה שלה.
  4. React DOM מעדכן ביעילות את ה-DOM להיות תואם ל-<h1>Hello, Sara</h1>.

הערה: התחילו תמיד שמות קומפוננטות עם אות גדולה.

React מתייחסת לקומפוננטות המתחילות באותיות קטנות בתור תגי DOM. לדוגמה, <div /> מייצג תג div של HTML, אך <Welcome /> מייצג קומפוננטה ומחייב את Welcome להיות ב-scope.

כדי ללמוד עוד על הסיבות מאחורי קונבנציה זו, אנא קראו את JSX באופן מעמיק.

יצירת קומפוננטות

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

למשל, אנו יכולים ליצור קומפוננטת App שמרנדרת את Welcome הרבה פעמים:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

function App() {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

נסו זאת ב-CodePen

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

חילוץ קומפוננטות

אל תפחדו לפצל קומפוננטות לקומפוננטות קטנות יותר.

למשל, הביטו בקומפוננטת Comment הבאה:

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <img className="Avatar"
          src={props.author.avatarUrl}
          alt={props.author.name}
        />
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

נסו זאת ב-CodePen

היא מקבלת את author (אובייקט), text (מחרוזת) ו-date (תאריך) בתור props, ומתארת תגובה באתר אינטרנט של מדיה חברתית.

קומפוננטה זו יכולה להיות מסובכת לשינוי בגלל כל הקינון שבה, ובנוסף קשה לעשות שימוש חוזר בחלקים אינדיבידואלים שלה. בואו נחלץ מספר קומפוננטות ממנה.

ראשית, נחלץ את Avatar:

function Avatar(props) {
  return (
    <img className="Avatar"
      src={props.user.avatarUrl}
      alt={props.user.name}
    />
  );
}

ה-Avatar לא צריך לדעת שהוא מתרנדר בתוך Comment. זו הסיבה שבגללה נתנו ל-prop שלו שם גנרי יותר: user (משתמש) ולא author (מחבר).

אנו ממליצים על מתן שמות ל-props מנקודת המבט של הקומפוננטה עצמה ולא על סמך הקונטקסט שבו הם נמצאים בשימוש.

אנו יכולים כעת לפשט Comment מעט:

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <Avatar user={props.author} />
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

בשלב הבא, נחלץ את קומפוננטת UserInfo שמרנדרת Avatar לצד שם המשתמש:

function UserInfo(props) {
  return (
    <div className="UserInfo">
      <Avatar user={props.user} />
      <div className="UserInfo-name">
        {props.user.name}
      </div>
    </div>
  );
}

זה מאפשר לנו לפשט את Comment אפילו יותר:

function Comment(props) {
  return (
    <div className="Comment">
      <UserInfo user={props.author} />
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

נסו זאת ב-CodePen

חילוץ קומפוננטות אולי נראה כמו עבודה שחורה בהתחלה, אבל בעלות על מגוון קומפוננטות לשימוש חוזר משתלמת בטפליקציות גדולות יותר. כלל אצבע טוב הוא שאם חלק מממשק המשתמש שלכם נמצא בשימוש מספר פעמים (Button, Panel, Avatar), או שהוא מורכב מספיק בכוחות עצמו (App, FeedStory, Comment), הוא מועמד טוב להיות קומפוננטה לשימוש חוזר.

Props הם לקריאה בלבד

בין אם אתם מצהירים על קומפוננטה כפונקציה או כמחלקה, אסור לה לעולם לשנות את ה-props שלה. הביטו בפונקציה sum הבאה:

function sum(a, b) {
  return a + b;
}

פונקציות אלה נקראות “טהורות” מכיוון שהן לא מנסות לשנות את הקלטים שלהן, ותמיד מחזירות את אותה התוצאה עבור אותם קלטים.

לעומת זאת, פונקציה זו אינה טהורה משום שהיא משנה את הקלט שלה:

function withdraw(account, amount) {
  account.total -= amount;
}

React היא די גמישה אבל יש לה כלל אחד נוקשה:

כל קומפוננטות React חייבות לפעול כמו פונקציות טהורות ביחס ל-props שלהן.

כמובן, ממשקי המשתמש של אפליקציות הם דינמיים ומשתנים עם הזמן. בחלק הבא, נציג את הקונספט החדש של “state” (מצב). ה-state מאפשר לקומפוננטות React לשנות את הפלט שלהן לאורך הזמן בתגובה לפעולות משתמש, תשובות מהרשת, וכל דבר אחר, מבלי להפר כלל זה.