תבנית סטארטר פשוטה ליישומי Flux

25/01/2016

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

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

1. למה בעצם פלאקס כל כך מסובך? (או: ממה מורכב יישום פלאקס).

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

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

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

אז הנה עוד רעיון: בואו נבנה אוביקטים נוספים החיצוניים לראיקט להם אפשר לקרוא מהפקדים השונים. המידע שצריך לשמור יישמר באותם רכיבים חיצוניים (נקרא להם ״מודלים״). מספר פקדי ריאקט בעצים שונים יוכלו לשתף ביניהם אוביקטים אלו ולבצע עליהם פעולות. המודלים יכולים להינתן לפקדי הריאקט כ SIngletons או לעבור מהשורש באמצעות המנגנון של State של השורש המועבר כ Properties לילדים. אבל זה כאמור לא הסוף.

פלאקס מתחיל בארכיטקטורת ה Model/View ומוסיף עליה שני רעיונות: האחד הוא הניתוק בין ״הפעולה״ לבין הפקד. בחירה באפשרות ״שמירה״ דרך תפריט, דרך Toolbar או באמצעות קיצור מקלדת אמורה לעשות בדיוק את אותו הדבר ולהריץ בדיוק את אותו הקוד, למרות שהפעולה יכולה להגיע ממספר פקדים שונים ולהשפיע על מספר מודלים שונים. הרעיון השני והקשור הוא קביעת סדר הטיפול בפעולה בין המודלים, כך שמודל אחד יוכל להצהיר שהוא תלוי במודל אחר שיטפל באירוע לפניו.

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

2. התמודדות של פלאקס עם ניתוק פעולה מפקד

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

function save(url, userData) {
  Dispatcher.dispatch({ type: START_SAVE_DOCUMENT, payload: userData });
  $.post(url, userData).
    then(function(res) {
      Dispatcher.dispatch({ type: DONE_SAVE_DOCUMENT });
    },
    function(err) {
      Dispatcher.dispatch({ type: ERR_SAVE_DOCUMENT });
    });
}

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

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

3. התמודדות של פלאקס עם מודלים והקשר ביניהם

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

import Store from '../store';
import Dispatcher from '../dispatcher';
import symbols from '../symbols';

const DocumentStore = Object.assign({}, Store, {
  editable: false,

  setEditable: function(val) {
    this.editable = val;
    this.trigger();
  },
});

DocumentStore.dispatchToken = Dispatcher.register(function(action) {
  switch(action.type) {
    // handle incoming events and modify store data
  case START_SAVE_DOCUMENT:
    DocumentStore.setEditable(false);
    break;
  case DONE_SAVE_DOCUMENT:
    DocumentStore.setEditable(true);
    break;

  case ERR_SAVE_DOCUMENT:
    DocumentStore.setEditable(true);
    break;
  }
});

export default UsersStore;

מבחינת המודל לא חשוב מהיכן הגיע הפעולה. מה שחשוב שעכשיו מתחילים לשמור מסמך או מסיימים לשמור מסמך. פונקציה נוספת בתוך ה Dispatcher נקראת waitFor והיא יכולה לקבל dispatchToken ממודל אחר וכך להצהיר שאנו מעוניינים לקבל את האירוע רק אחרי שמודל אחר יסיים לטפל בו.

4. רגע ומה עם הסטארטר מהכותרת?

עכשיו שהבנו בגדול איך פלאקס עובד אתם מוכנים לקבל תבנית התחלה סופר פשוטה ליישום פלאקס:
https://github.com/ynonp/flux-tiny-starter

התבנית מגדירה תיקייה עבור actions, תיקיה נוספת עבור stores ותיקיה שלישית עבור React Components.

יש גם קובץ Actions ו Store התחלתיים. בכל פעם שתרצו להוסיף פעולה הוסיפו את קוד הפעולה כ Action ואת המידע עליו היא משפיעה ל Store. כשהיישום יגדל כנראה תרצו לשבור את ה Store למודלים נוספים ואולי להוסיף אוביקטי Actions נוספים בקבצים חדשים. כל הקבצים בתבנית מתועדים והקוד שיש שם כבר עובד ומתוכנן כך שיהיה קל להרחיב אותו.

ספרו מה דעתכם בתגובות או שלחו Pull Request עם הצעות לשיפור.