• בלוג
  • קוד התחלה טוב יותר ליישומי Redux

קוד התחלה טוב יותר ליישומי Redux

15/06/2017

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

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

1. הדוגמא מהספר

נתחיל עם הדוגמא מהספר:

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    case ADD_TODO:
      return Object.assign({}, state, {
        todos: [
          ...state.todos,
          {
            text: action.text,
            completed: false
          }
        ]
      })    
    default:
      return state
  }
}

זה Reducer שלקחתי ישירות מהתיעוד של רידאקס. הוא עוזר להבין את העיקרון של Reducer, אבל עובד די גרוע כבסיס למערכת אמיתית. קוד כזה אומר שבכל פעם שרוצים להוסיף פעולה יש להוסיף קוד ב-3 מקומות:

  1. יש ליצור Action Type חדש, כלומר להוסיף קבוע (כמו הקבועים SET_VISIBILITY_FILTER ו ADD_TODO).

  2. יש ליצור Action Creator חדש כדי ליצור אוביקט פעולה:

function actVisibilityFilter(filter) {
    return { type: SET_VISIBILITY_FILTER, payload: filter };
}
  1. ויש להוסיף בלוק רלוונטי ב Reducer. בנוסף בחיים האמיתיים הפעולות הופכות לארוכות וזה נוח שהקוד שמטפל בפעולה יהיה בפונקציה משלו.

2. איך הייתי מעדיף שיראה Reducer

כל Reducer מגדיר קוד טיפול באירועים, ואת המיפוי בין שם האירוע לקוד הטיפול בו. הייתי מעדיף שהמיפוי הזה יעשה אוטומטית ובשביל זה אפשר לשנות קצת את קוד ה switch בגוף הפונקציה:

let handlers;
let PREFIX = 'TODOS.';

const SET_VISIBILITY_FILTER = 'TODOS.setVisibilityFilter';
const ADD_TODO = 'TODOS.addTodo';

function todoApp(state = initialState, action) {
  if (action.type.startsWith(PREFIX)) {
    const handlerName = action.type.split('.').pop();
    if (typeof handlers[handlerName] === 'function') {
      return handlers[handlerName](state, action);
    }
  }
  return state;
}

handlers = {
  setVisibilityFilter(state, action) {
    return Object.assign({}, state, {
      visibilityFilter: action.filter
    })
  },

  addTodo(state, action) {
    return Object.assign({}, state, {
      todos: [
        ...state.todos,
        {
          text: action.text,
          completed: false
        }
      ]
    })
  }
};

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

3. שיפור קוד ה Actions

גם בצד של ה Actions אפשר לייעל דברים, וליצור אוטומטית Action Creator. נתבונן בקוד הבא:

const actions = {};
function symbol(name) {
  actions[name] = payload => ({ type: PREFIX + name, payload });
  return PREFIX + name;
}

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

const SET_VISIBILITY_FILTER = symbol('setVisibilityFilter');
const ADD_TODO = symbol('addTodo');

ומתוך קוד הפקד להפעיל:

dispatch(actions.setVisibilityFilter({ filter: 'hello' });

4. שיפורים נוספים

הפונקציה symbol היא רק דוגמא ליצירה אוטומטית של קבועים ופעולות. כמוה אפשר לייצר פונקציות נוספות עבור מקרים נוספים. דוגמא קלאסית היא Ajax:

function ajaxSymbol(name, url) {
  actions[name] = function() {
    return (dispatch, getstate) => {
      dispatch({ type: PREFIX + name + '-START' });
      $.get(url)
      .then(function(res) {
        dispatch({ type: PREFIX + name + '-END', payload: res });
      })
      .fail(function(err) {
        dispatch({ type: PREFIX + name + '-FAIL', payload: err });
      });
    };
  };

  return {
    start: PREFIX + name + '-START',
    end: PREFIX + name + '-END',
    success: PREFIX + name + '-FAIL',
  };
}

וכך קיבלנו 3 שמות של פעולות, פונקצית Action Creator שמייצרת בקשת Ajax ואוטומטית תפעיל את הפעולות המתאימות בעת התחלה, סיום וכשלון. הקריאה:

const LOAD_TODOS_FROM_SERVER = ajaxSymbol('loadTodos', '/todos');

מייצרת פעולה ו-3 שמות שנוכל אחר כך להשתמש בהם ב Reducer.