Refactoring Reducers - Tuong-Nguyen/JavaScript-Structure GitHub Wiki

Reusable utility functions

  • Utilities to handle the "slice-of-state" logic for the top-level app reducer
  • Put all functions to a single file reducerUtilities.js

updateObject

function updateObject(oldObject, newValues) {
    // Encapsulate the idea of passing a new object as the first parameter
    // to Object.assign to ensure we correctly copy data instead of mutating
    return Object.assign({}, oldObject, newValues);
}

updateItemInArray

function updateItemInArray(array, itemId, updateItemCallback) {
    const updatedItems = array.map(item => {
        if(item.id !== itemId) {
            // Since we only want to update one item, preserve all others as they are now
            return item;
        }

        // Use the provided callback to create an updated item
        const updatedItem = updateItemCallback(item);
        return updatedItem;
    });

    return updatedItems;
}

createReducer

function createReducer(initialState, handlers) {
  return function reducer(state = initialState, action) {
    if (handlers.hasOwnProperty(action.type)) {
      return handlers[action.type](state, action)
    } else {
      return state
    }
  }
}

How to use

  • Move each case in reducer to a function
// Case reducer
function addTodo(todosState, action) {
    const newTodos = todosState.concat({
        id: action.id,
        text: action.text,
        completed: false
    });

    return newTodos;
}

// Case reducer
function editTodo(todosState, action) {
    const newTodos = updateItemInArray(todosState, action.id, todo => {
        return updateObject(todo, {text : action.text});
    });

    return newTodos;
}
  • Use createReducer function to slide reducer
const todosReducer = createReducer([], {
    'ADD_TODO' : addTodo,
    'EDIT_TODO' : editTodo
});
  • Combine reducers as usual in rootReducer.js
// "Root reducer"
const appReducer = combineReducers({
    // ...
    todos : todosReducer
});