שלום אורח התחבר

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

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

רידאקס הוא אחלה אבל כמות הקוד מסביב שצריך לכתוב רק בשביל שדברים יעבדו יכולה להעיק, במיוחד בעת פיתוח פיצ'רים חדשים. בצד הטוב עם קצת יצירתיות ו 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.


נהניתם מהפוסט? מוזמנים לשתף ולהגיב