חיפוש
  • יניב אור

מערכת ניהול - React - חלק 6

בחלק זה אתמקד ב-reducers שמעבדים את הנתונים הקשורים כרגע לטבלה - לכל אחת מהישויות accounts ו-posts.


את ה-initialState שהיה קודם לכן כקובץ שיניתי לתיקיה ובתוכה קובץ index שמאגד באובייקט אחד מספר ישויות שמוגדרות בקובץ נפרד לכל אחת.


src/store/reducers/entities/initialState/index.js


src/store/reducers/entities/initialState/accounts.js


src/store/reducers/entities/initialState/posts.js


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


אז ב-state של כל ישות יש כותרת, הגדרות לטבלה, הגדרות לטופס ו-rows שיחזיק את כל המידע מהשרת. בזמן טעינת המערכת, עוד בשלב הניתוב (CustomRoute) - אני טוען את המידע של accounts ו-posts משרתי ה-API בעזרת הפונקציה getItems.


src/store/actions/entities/index.js

נשלחת קריאה לאקשן getItemsStarted שמודיע על התחלת תהליך שליפת הנתונים. לאחר מכן נשלחת בקשה לשרת API של ה-entity המסויים בו מטפלים. ב-header יש JWT token שנאסף בעזרת הפונקציה authHeader. אם הקריאה נכשלה, נשלחת קריאה לאקשן getItemsFailure. אם הקריאה הצליחה ויש data, נשלחות קריאות לשני אקשנים: getItemsSuccess ו-getProcessedItems. רק לאחר שהאקשן הראשון (getItemsSuccess) עבר דרך ה-reducers וה-state התעדכן - רק אז מתחיל לפעול האקשן השני. חשוב לזכור - כך עובד ה-dispatch. נעבור ל-reducers והתהליך יהיה ברור יותר.


src/store/reducers/entities/index.js

זה הקובץ הראשי של ה-reducers שקשורים ב-entities. הקובץ הזה ממפה בין האקשנים לפונקציות כשכל פונקציה מחזירה לפחות את ה-state כמו שהוא. אפשר, אגב, להשוות ל: מערכת ניהול - React - חלק 4. הפונקציות נמצאות בקובץ הבא ופונקציות עזר נוספות נמצאות בקובץ process.js.


src/store/reducers/entities/main.js


src/store/reducers/entities/process.js

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


נחזור ל-main.js - כרגע אנחנו מתעסקים עם:


הפונקציה מקבלת את ה-state הנוכחי, את שם ה-entity ואת ה-payload שעובר destructuring וממנו נשלף rows, שהוא המידע מהשרת והוא מערך של אובייקטים. אני משתמש בפונקציה arrToHash כדי להפוך את מערך האובייקטים הזה לסוג של hash table שבעולם של Javascript זה אובייקט שמכיל אובייקטים עם מפתח וכולי. הפונקציה מקבלת מערך של אובייקטים ומבצעת צמצום (reduce) לאובייקט של אובייקטים - אותו אנחנו מעוניינים לעדכן ב-state, ב-rows. המערך אובייקטים עובר תהליך נורמליזציה שיוצר בו אינדקסים. תהליך דומה, בגדול, להוספת אינדקסים ב-SQL, למשל.



כמובן ש-state לא מעדכנים ישירות. מה שעושים זה יוצרים עותק שלו:

אני מזכיר שהוא נראה כך:

אנחנו כרגע מתעסקים עם ה-reducer של entities והילדים הם האובייקטים accounts ו-posts - כשאלו גם המפתחות. לכן השימוש במשתנה entity שמחזיק את אחד המפתחות:

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


האקשן הבא בתור להוציא לפועל הוא:

לאחר שאספתי את הנתונים מהשרת ועשיתי למערך אובייקטים תהליך נורמליזציה שיצר בו אינדקסים - הוא מוכן לעבודה. בפונקציה getProcessedItems אני מכין את ה-state לעבודה ספציפית עם הטבלה שתוצג בהמשך על המסך. זאת טעות שאני קורא לפונקציה הזאת מיד אחרי הבאת הנתונים -עוד בשלב הניתובים - לפני כניסה לדף שמציג את הטבלה. אתקן בהמשך ואעביר אותה לרכיב ה-Entity שמכיל, בין היתר, את הטבלה, הטופס וכו'.


לאקשן getProcessedItems יש תפקיד חשוב ואם תשימו לב - בקובץ actions של entities שהוצג למעלה - אני קורא לרוץ לו כמעט אחרי כל אקשן אחר. שאר האקשנים מעדכנים את ה-state בפרטים שקשורים למשל, לשינוי מספר העמוד הנוכחי בטבלה (המשתנה page), עדכון אובייקט המיונים (האובייקט sort), חיפוש (המשתנה q) וכן הלאה. ברגע שאחד הפרמטרים האלה השתנה ב-state - אני שוב רץ על כל המידע שקיבלתי ואותו נירמלתי - ומבצע עליו את הפעולות של הסינון, מיון, דפדוף וכו'.


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


החלק הרלוונטי מתוך הקובץ:

src/store/reducers/entities/process.js

המשתנה nextItems מחזיק את המיקום של קבוצת השורות (rows) שצריך להציג. בהמשך אני משתמש בו בפונקציה slice והוא למעשה הוגדר בחלק הזה של הפונקציה לצורך שימוש בהמשך - למניעת כפילות של קוד. בכל מקרה - ולמרות שהקוד הרבה יותר נקי ממה שהיה לפני ה-code refactoring האחרון - זה נראה כמו לכלוך בתחילת הפונקציה, אני לא אוהב את זה. סיבה נוספת לטפל בפרט הזה שכרגע נראה קטן אבל ככל שלתוכנה תתווסף סיבוכיות, כך אצטרך לפתוח עוד משתנים כאלה והקוד יראה כמו מזבלה. מתחילת הפרויקט, רוב הזמן עבדתי ללא תכנון והקוד השתנה והתנקה במהלך העבודה. לפני הניקוי הרציני שהוזכר, ישבתי קצת ותכננתי יותר לעומק איך יראו הדברים.


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


נעבור לתהליך עצמו של עיבוד המידע. אגב, הוא נחתך לשני משתנים ועל כך בהמשך.


אני מתחיל עם constructor של Object וממיר את המידע שקיבלתי למערך של האובייקטים - בעזרת values. אפשר להשתמש ב-keys, למשל, כדי לקבל מערך של האינדקסים. אנחנו צריכים לבצע פעולות על המידע עצמו.


השלב הבא הוא סינון על פי מילת החיפוש שנמצאת ב-store, ב-q - בעזרת הפונקציה filter. מבחינת ניקיון הקוד, כבר רואים התחלה של לכלוך. אין שום סיבה שקטע קוד שאחראי על פונקציונליות מסוימת יהיה באותו scope יחד עם קטעי קוד אחרים. סביר להניח שאצטרך בדיוק את אותו קטע קוד במקומות אחרים ושוב אנחנו מגיעים לקובץ פונקציות נוסף, כללי יותר. בכל אופן, יהיה קל לטפל בזה בהמשך משום שהכל מרוכז ב-reducers ולא מסתובב ברכיבים השונים במערכת.


הסיבה שחתכתי את הקוד בשלב הזה היא כי אני צריך לדעת מה כמות האייטמים לאחר הסינון של מילת החיפוש. זה יהיה הערך count ב-state. בהמשך יש סינון נוסף לפי עמודים - ולא תהיה לי שום דרך לדעת את הכמות הזאת, אלא אם כן אשתמש שוב ב-filter ואעשה שוב את אותה עבודה שנעשתה כבר. אני מחזיר את הכמות הזאת כ-filteredRows.length.


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


הדבר הבא הוא טיפול בדפדוף בעזרת הפונקציה slice.


לבסוף מצמצמים עם reduce למערך של אינדקסים שיתארו בהמשך לרכיבים אילו שורות להציג מ-rows המנורמל. בנוסף למערך אינדקסים, אני מצרף גם את חישוב סכום השורות הכולל, לאחר סינון.



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


סיימנו עם getRowIds - פונקציה חשובה שאני קורא לה בכל שינוי של פרמטרים ב-state שקשורים לטבלה.



המשך בחלק הבא:

מערכת ניהול - React - חלק 7


© 2023 by DO IT YOURSELF. Proudly created with Wix.com