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

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

עודכן ב: מרץ 21

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

לפני כן, עשיתי כמה תיקונים שקשורים ל-reducers. אפרט על התיקון ונחזור למיונים.


מיון

לצורך תזכורת, כך נראה קובץ ה-initialState:


src/reducers/entity/initialState.js



קובץ ה-reducers של Entity - לפני התיקון ולפני שהוספתי טיפול באקשן SORT_TABLE - נראה כך:


src/reducers/entity/index.js

יש פה בעיית scope. לכל case יש את הפונקציונליות שלו עם המשתנים שלו וכן הלאה אך מכיוון שכולם נמצאים באותו scope שהוא הפונקציה entity - יש אפשרות מאוד סבירה שיהיו הגדרות כפולות של משתנים וכדומה, לכן כל בלוק כזה צריך לוודא את העניינים האלה. בכל אופן, זה קוד לא מסודר.


הקובץ המסודר והחדש נראה כך:


src/reducers/entity/index.js

כל מקרה (case) קורא לפונקציה - גם אם הפונקציה מחזירה את ה-state כמו שהוא ולא מבצעת שום פעולה נוספת.


קובץ הפונקציות נקרא main.js והוא נראה כך:


src/reducers/entity/main.js

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


על-פי ה-querymen, אני בונה את הסינון בצורה כזאת. שדה ה-sort ב-store יהיה אובייקט שיכיל מפתח וערך. המפתח הוא שם של אחד השדות והערך יכול להיות 1 ל-ASC או מינוס 1 ל-DESC.


sort: { username: 1, createdAt: -1 }


נעבור על הפונקציה sortTable בקובץ main.js. דבר ראשון אני אוסף את ה-state של sort ושל rows. את ה-sort אני מעדכן בהתאם ל-field שקיבלתי. אם השדה לא קיים באובייקט של המיון, אני מוסיף אותו עם הערך 1 - אחרת, אם הערך שלו היה 1 אני משנה למינוס 1 - אחרת, אני מוחק אותו מהאובייקט.


את rows - המידע עצמו שהוא מערך של אובייקטים - אני מעביר בפונקציה sort שבתורה מפעילה פונקציית השוואה. כדי להסביר את פונקציית ההשוואה - אתן דוגמא פשוטה יותר.


ניקח לדוגמא את מערך העצמים הבא:

אם נדפיס את rows ל-console נקבל:

כדי למיין לפי username, למשל, נשתמש בפונקציית sort וב-localeCompare - בצורה הבאה:

אם נדפיס את rows נקבל:

כדי למיין לפי username ובנוסף בהמשך לפי email, נכתוב זאת כך:

התוצאה תהיה:


מה שנשאר זה לבנות את השורות הללו של ההשוואה בצורה דינאמית לפי השדות שמתעדכנים באובייקט sort. דבר נוסף זה להוסיף טיפול ב-DESC או מינוס 1 - כיוון הפוך של המיון. את זה אני עושה בהיפוך a ו-b. וזה נראה כך:


נעבור לחלק של ה-UI, לרכיב Head של הטבלה שמסומן באדום ונראה כך:


הקוד של רכיב ה-Head:

src/components/Common/Table/Head.js


בעת לחיצה על אחד מראשי השדות, שם השדה עובר דרך הפונקציה sortTable דרך Table ו-Entity עד לטיפול ה-reducer. דבר נוסף שקורה זה שינוי בחץ השחור הקטן שנמצא לצד כל שדה, במידה והוא פעיל. ב-CSS ברכיב שקיבל את השם Arrow - אני משנה את ה-transform בהתאם ל-status שמגיע בסופו של דבר מה-store - באובייקט sort. הכיוון הרגיל - ללא transformation - הוא חץ למטה. ה-status הוא 1- (מינוס אחת) וה-transform שלו הוא rotate(0deg) כלומר, להשאר במצב הנוכחי שלו. אם ה-status הוא 1 - אני רוצה שהחץ יופיע עם כיוון כלפי מעלה ולכן מגדיר לו rotate(180deg). אם הסטטוס שלו null - אני מגדיר לו scale(0) שלמעשה מעלים את החץ מהמסך.



סינון

הוספתי שדה טקסט לחיפוש, לרכיב ה-View של Entity וחיברתי אותו ל-action שנקרא searchTable - שמתבצע בכל אירוע שינוי בשדה הטקסט - ומשם ל-reducer ל-פונקציה שנקראת searchTable גם היא. נתקן את עניין השמות הללו בהמשך ונסביר עיקרון חשוב בנושא.


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


מה שעשיתי בסופו של דבר היה להגדיר שני מערכים ב-initialState, לטובת המידע שמגיע מהשרת: rows ו-filteredRows. שניהם ימולאו במידע מהשרת. filteredRows יהיה המערך האקטיבי. הוא יכיל את המידע המסונן, הממוין וכו'. rows יכיל תמיד את המידע המקורי.


ה-reducer נראה כעת כך:

src/reducers/entity/main.js

ב-getItemsSuccess, כשהמידע התקבל מהשרת, ה-state מתעדכן בצורה כזאת ש-rows ו-filteredRows - שניהם נטענים במידע המלא - rows.


ב-sortTable - המיון של המידע מתבצע על filteredRows.


ב-searchTable - תוצאות החיפוש - פעולת הסינון - מתבצעת על rows כשהתוצאה מתעדכנת ב-filteredRows.


הרכיב View של Entity:


src/components/Views/Entity.js



הוספתי שדה טקסט לחיפוש ועטפתי אותו ואת הכותרת הראשית ברכיב שקראתי לו TopBar. שדה טקסט פשוט שב-onChange מעביר את המחרוזת לאקשן searchTable.



עדכון שמות פונקציות

שיניתי שמות של שתי פונקציות ב-reducers של Entity. את sortTable ל-sortData ואת searchTable ל-searchData. התיקון הזה מדגיש שלא חייב להיות קשר בין הטבלה כרכיב UI לבין המידע עצמו שנמצא ב-store - והפעולה sortTable מובילה ל-sortTable כשהאחרונה יכולה להיקרא ע"י פעולות אחרות, לצורך העניין.


תיקונים נוספים ל-reducers

קודם לכן עשיתי סדר בקוד שקשור ל-reducers. השארתי בקובץ src/reducers/entity/index.js רק את ה-switch שבורר בין ה-actions וקורא לפונקציה המתאימה מתוך קובץ של פונקציות. הקובץ הוא src/reducers/entity/main.js.


אם נסתכל על הקובץ הזה (מופיע למעלה), גם הוא לא כל כך מסודר. רוב הקובץ מורכב מהפונקציות שמתאימות באופן ישיר ל-actions. חלקן קטנות ופשוטות ומעדכנות את ה-state ללא חישובים או פעולות נוספות. אחת מהן (changePage) משתמשת בפונקציית עזר (getPage) למשל, שמופיעה בתחתית הדף תחת הכותרת /* Private */. אחרות מתחילות להיות מורכבות יותר והקובץ מתחיל להיות מבולגן. אפשר לראות לפי הצבעים והצורה, בין היתר.


פתחתי קובץ חדש בשם process.js ובו יתבצעו כל החישובים. הנה הקבצים המעודכנים.


src/reducers/entity/main.js


src/reducers/entity/process.js


תיקון באג

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


החלק הרלוונטי מתוך src/reducers/entity/main.js


החלק הרלוונטי מתוך src/reducers/entity/process.js



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

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

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