Thinking in React
React, לפי דעתנו, היא ספריית הג’וואה-סקריפט המובילה בבניית יישומי אינטרנט. הספרייה הוכיחה את עצמה אצלנו בפייסבוק ובאינסטגרם.
אחד מהיתרונות הבולטים של React הוא איך הוא גורם לך לחשוב על היישומים בזמן שאתה בונה אותם. במדריך זה, נלווה אותך בתהליך החשיבה של בניית טבלת נותני מוצרים באמצעות React.
נתחיל עם דוגמא
נניח שיש לנו ממשק JSON שנראה ככה:
הממשק מחזיר מידע בפורמט JSON שנראה ככה:
[
{category: "Sporting Goods", price: "$49.99", stocked: true, name: "Football"},
{category: "Sporting Goods", price: "$9.99", stocked: true, name: "Baseball"},
{category: "Sporting Goods", price: "$29.99", stocked: false, name: "Basketball"},
{category: "Electronics", price: "$99.99", stocked: true, name: "iPod Touch"},
{category: "Electronics", price: "$399.99", stocked: false, name: "iPhone 5"},
{category: "Electronics", price: "$199.99", stocked: true, name: "Nexus 7"}
];
השלב הראשון: פיצול ממשק המשתמש להיררכיית קומפוננטות
הדבר הראשון שנעשה הוא להקיף כל קומפוננטה(ותת קומפוננטה) בקופסה, ולתת לכל אחת שם. אם אתם עובדים עם מעצב/ת , יכול להיות שהוא/היא כבר עשו את זה.
אבל איך יודעים מה צריך להיות קומפוננטה משלו? משתמשים באותה טכניקה שבה מחליטים אם ליצור מתודה או עצם. אחת השיטות היא עיקרון האחריות הבודדת, שאומר, שאופן אידיאלי כל קומפוננטה אמורה לעשות דבר אחד בלבד. אם הקומפוננטה גדלה, נצטרך לפצל אותה לתת-קומפוננטות.
משום שלעיתים תכופות נצטרך להציג מידע בפורמט JSON למשתמש, נראה שאם המודל נבנה כראוי, ממשק המשתמש שלנו( וגם מבנה הקומפוננטות ) ימופה באופן מסודר. זה קורה מכיוון שממשק המשתמש ומודלי המידע נוטים לדבוק באותה ארכיטקטורת מידע. פצל את ממשק המשתמש שלך לקומפוננטות, שכל אחת מהן מתאימה לחלק אחד של מודל המידע שלך.
נראה כאן שיש לנו חמש קומפוננטות ביישום שלנו. המידע שכל קומפוננטה מייצגת הוא בפונט italic
FilterableProductTable
(כתום): מכיל את כל תכולת הדוגמהSearchBar
(כחול): קלט המשתמשProductTable
(ירוק): מציג ומסנן את המידע על סמך קלט המשתמשProductCategoryRow
(טורקיז): מציג כותרת לכל קטגוריהProductRow
(אדום): מציג שורה לכל מוצר
אם נסתכל על ProductTable, נראה שכותרת הטבלה( שמכילה את כותרות השם והמחיר ) היא לא קומפוננטה משלה. אפשר להפוך אותה לקומפוננטה משלה ואפשר גם שלא, זה עניין של בחירה. בדוגמה זאת, השארנו אותו חלק מProductTable בגלל שזה חלק מרינדור המידע, שהוא עבודת הקומפוננטה ProductTable. לעומת זאת, אם הכותרת גדלה ונהיית מסובכת( לדוגמה אם היינו מוסיפים חיפוש ), זה בהחלט יעלה צורך לפצל אותה לקומפוננטה משלה( ProductTableHeader ). עכשיו לאחר שזיהינו את הקומפוננטות בדוגמה שלנו, נסדר אותם בהיררכיה. קומפוננטות שנמצאות בתוך קומפוננטות אחרות צריכות להופיע מתחתיהן בהיררכיה:
Step 2: בניית גרסה סטטית בReact
See the Pen Thinking In React: Step 2 on CodePen.
עכשיו שיש לנו את היררכיית הקומפוננטות שלנו, זה הזמן ליישם את היישום. הדרך הקלה ביותר היא לבנות גרסה שלוקחת את מודל המידע ומרנדרת את ממשק המשתמש, אבל אין לה אינטראקטיביות. מומלץ לפצל תהליכים אלו בגלל שבניית גרסה סטטית דורשת הרבה הקלדה ומינימום חשיבה, והוספת אינטראקטיביות דורשת הרבה חשיבה ומינימום הקלדה. נראה למה.
על מנת לבנות גרסה סטטית של היישום שמרנדרת את מודל המידע, נרצה לבנות קומפוננטות שמשתמשות בקומפוננטות אחרות ומעבירות מידע באמצעות props.props הם דרך העברת מידע מ’הורה’ ל’ילד’. אם אתם מכירים את הקונספט של state, **אל תשתמשו בstate בכלל על מנת לבנות את הגרסה הסטטית. state שמור רק לאינטראקטיביות, שזה מידע שמשתנה לאורך זמן. מכיוון שזאתי גרסה סטטית של היישום, לא נצטרך state.
נוכל לבנות מלמעלה למטה או מלמטה למעלה. שזה אומר שנוכל להתחיל לבנות את הקומפוננטות מלמעלה בהירככיה( נתחיל מFilterableProductTable ) או מהקומפוננטות התחתונות בהיררכיה( ProductRow ). בדוגמות פשוטות יותר, זה קל יותר להתחיל מלמעלה למטה, ובפרויקטים גדולים, זה קל יותר להתחיל מלמטה למעלה ולכתוב tests במקביל לבנייה.
<<<<<<< HEAD
בסוף השלב הזה, יהיה לנו ספרייה של קומפוננטות שמישות שמרנדרות את מודל המידע. הקומפוננטות יכילו רק מתודות render() מכיון שזוהי גרסה סטטית של היישום. הקומפוננטה בראש ההיררכיה(FilterableProductTable) תיקח את מודל המידע כprop שיועבר לה. אם נבצע שינוי למודל המידע ונקרא ל ReactDOM.render() שוב, ממשק המשתמש יעודכן. זה פשוט לראות איך ממשק המשתמש מעודכן והיכן לבצע שינויים מכיוון ששום דבר מסובך לא מתבצע. העברת המידע בכיוון אחד של React משאירה הכל בצורה מודולרית ומהירה.
=======
At the end of this step, you’ll have a library of reusable components that render your data model. The components will only have render()
methods since this is a static version of your app. The component at the top of the hierarchy (FilterableProductTable
) will take your data model as a prop. If you make a change to your underlying data model and call root.render()
again, the UI will be updated. You can see how your UI is updated and where to make changes. React’s one-way data flow (also called one-way binding) keeps everything modular and fast.
84ad3308338e2bb819f4f24fa8e9dfeeffaa970b
קרא עוד בתיעוד של ריאקט אם תצטרך עזרה בביצוע שלב זה.
הפוגה קצרה: props ו- state
יש שני סוגים של ‘מודלי’ מידע בReact : props וstate. זה חשוב להבין את ההבדלים בין השניים. קרא עוד בתיעוד של ריאקט אם אתה לא בטוח מה ההבדל. ראה גם שאלות נפוצות: מה ההבדל בין state ו-props?
שלב שלישי: זיהוי הייצוג המינימלי( אך מלא ) של הstate של ממשק המשתמש
על מנת שממשק המשתמש יהיה אינטראקטיבי, נצטרך שיהיה לנו את היכולת לשנות את מודל המידע שלנו. React עושה זאת עם state.
כדי לבנות את היישום בצורה נכונה, צריך לחשוב תחילה על הכמות המינימלית של state שהיישום דורש. המפתח פה הוא DRY: Don’t Repeat Yourself שאומר ‘אל תמחזר קוד’. נחשוב על הכמות המינימלית ביותר של state שהיישום צריך ונחשב כל דבר אחר שהיישום צריך לפי דרישה. לדוגמה, אם אנו בונים יישום של רשימת מטלות, יהיה לנו מערך של מטלות; לא נשאיר משתנה בstate בשביל הכמות מטלות. במקום זה, כשנרצה לרנדר את מספר המטלות, פשוט ניקח את אורך המערך ונציג אותו.
נראה את פיסות המידע ביישום שלנו, יש לנו את:
- הרשימה המקורית של המוצרים
- מילות החיפוש שהמשתמש הקליד
- ערך תיבת הסימון
- הרשימה המסוננת של המוצרים
נעבור על כל אחד ונחשוב איזה פיסת מידע היא הstate. נעשה זאת באמצעות שאילת 3 שאלות על כל פיסת מידע:
- האם המידע עובר מהורה באמצעות props? אם כן, זה לא state.
- האם זה נשאר קבוע לאורך זמן? אם כן, זה לא state.
- האם אפשר לחשב את זה לפי state אחר או props בקומפוננטה שלנו? אם כן, זה לא state.
הרשימה המקורית עוברת באמצעות props, אז היא לא הstate. טקסט החיפוש ותיבת הסימון נראים כמתאימים להיות state מכיוון שהם משתנים לאורך זמן ולא מושפעים ממשהו. ולבסוף, הרשימה המסוננת של המוצרים היא לא הstate מכיוון שהיא מושפעת מהרשימה המקורית של המוצרים, טקסט החיפוש וערך תיבת הסימון( מסומן או לא).
אז הגענו למסקנה שהstate שלנו הוא:
- טקסט החיפוש שהמשתמש הקליד
- ערך תיבת הסימון
שלב רביעי: זיהוי המיקום המתאים לstate
See the Pen Thinking In React: Step 4 on CodePen.
זיהינו מה המרכיבים המינימליים של הstate של היישום שלנו. עכשיו אנחנו צריכים לזהות איזה קומפוננטה משנה, או לוקחת בעלות על הstate.
זכרו: בReact הכל סובב סביב העברת מידע למטה בהיררכיית הקומפוננטות. זה לפעמים לא ברור מיד איזה קומפוננטה הכי מתאימה לאחסן בתוכה state. זה לעיתים קרובות החלק הכי מאתגר למתחילים בReact, לכן נעבור על השלבים הבאים על מנת להבין:
לכל פיסת state ביישום:
- נזהה כל קומפוננטה שמרנדרת משהו מהstate.
- נמצא אם יש קומפוננטה אחרת למעלה בהיררכיה או קומפוננטה דומה שמתאימה לאחסן את הstate.
- אם לא נמצא קומפוננטה שמתאימה לאחסן את הstate, ניצור אחת חדשה שכל מטרתה היא לאחסן את הstate ונכניס אותה איפשהו בהיררכיה מעל קומפוננטה אחרת שמשתמשת בstate.
נשתמש בשלבים אלה ביישום:
- ProductTable צריכה לסנן את רשימת המוצרים לפי הstate וSearchBar צריך להציג את טקסט החיפוש ותיבת הסימון.
- הקומפוננטה השנייה שעושה שימוש בstate היא FilterableProductTable.
- באופן עקרוני זה הגיוני שהטקסט המסונן והערך המסומן ‘יחיו’ בתוך FilterableProductTable.
אז החלטנו שהstate ‘חי’ בתוך FilterableProductTable. ראשית, נוסיף אובייקט this.state = {filterText: ”, inStockOnly: false} לFilterableProductTable קונסטרקטור שלו על מנת לשקף את הstate הראשוני של היישום. אחרי זה, נעביר את filterText וinStockOnly לProductTable ו SearchBar כprop. לבסוף, נשתמש בprops האלה על מנת לסנן את השורות בProductTable ולהגדיר את הערכים של השדות בטופס בSearchBar.
אפשר כבר להתחיל לראות איך היישום שלנו יתנהג: נגדיר את filterText ל”כדור” ונרענן את היישום. נראה שטבלת המידע מתעדכנת עם הערך הנכון.
שלב חמישי: הוספת זרימת מידע הפוכה
See the Pen Thinking In React: Step 5 on CodePen.
עד כה, בנינו יישום שמרנדר כפונקציה של props וstate למטה בהיררכיה. עכשיו זה הזמן להוסיף אמצעי לזרימה הפוכה של מידע: קומפוננטות הטופס עמוק בהירככיה צריכות לעדכן את הstate בFilterableProductTable.
React הופך את זרימת המידע למפורשת על מנת לעזור לך להבין איך היישום שלנו עובד, אבל זה דורש טיפה יותר שורות קוד מאשר binding דו כיווני של נתונים.
אם ננסה לכתוב או לסמן את הקופסה בגרסה הקודמת של הדוגמא שלנו (שלב 4), נראה שReact מתעלם מהקלט שלנו. זה נעשה בכוונה, מכיוון שאנחנו הגדרנו את ערך הprop של הקלט להיות תמיד שווה לstate שמועבר מFilterableProductTable.
נחשוב לרגע על מה אנחנו רוצים שיקרה. אנחנו רוצים להיות בטוחים שכשהמשתמש משנה את הטופס, אנחנו מעדכנים את הstate שישקף את קלט המשתמש. מכיוון שרצוי שקומפוננטות יעדכנו את הstate שלהן בלבד, FilterableProductTable יעביר callbacks לSearchBar שיופעלו כשהstate יתעדכן. אנחנו יכולים להשתמש בonChange event על הקלטים. הcallbacks שעוברים דרך FilterableProductTable יקראו למתודת setState(), והיישום יתעדכן.
אפילו שזה נשמע מסובך, זה בסך הכל מספר מצומצם של שורות קוד. והיתרון הוא שזה מציג באופן מפורש איך המידע מועבר ביישום.
וזהו!
הכוונה של המדריך הזה היא לתת לכם רעיון על איך לחשוב על בניית קומפוננטות ויישומים עם React. אמנם זה יכול לצרוך יותר כתיבה ממה שאתם רגילים, אבל זכרו שקוד נקרא יותר מאשר הוא נכתב, וזה נוח מאוד לקרוא את הקוד המפורש והמודולרי שכתבנו. כשתתחילו לבנות ספריות גדולות של קומפוננטות, אתם תעריכו את הפשטות והמודולריות, ועם שימוש חוזר בקוד שאתם רושמים, שורות הקוד שלכם יתחילו להתכווץ. 😊