waynedunkley
4/4/2018 - 8:10 AM

Leverage localStorage to cache REDUX store data for slow API calls

Leverage localStorage to cache REDUX store data for slow API calls

const LIST_REQUEST_SENT = 'LIST_REQUEST_SENT'
const LIST_REQUEST_SUCCESS = 'LIST_REQUEST_SUCCESS'
const UPDATE_LIST = 'UPDATE_LIST'
const LIST_ADD_ITEM = 'LIST_ADD_ITEM'

const LOCAL_STORAGE_EXPIRATION_PERIOD = 604800000 // 7 days

const listRequestSent = () => ({
  type: LIST_REQUEST_SENT,
})

const listRequestSuccess = () => ({
  type: LIST_REQUEST_SUCCESS,
})

const updateList = list => ({
  type: UPDATE_LIST,
  list,
})

const listAddItem = (item) => ({
  type: LIST_ADD_ITEM,
  item,
})


/**
 * Handle fetching and storing of list items
 * 
 */
export const handleGetList = () => (
  (dispatch, getState) => {
    const { items } = getState().list

    // only if list is empty do we want to set the loading state
    if (items.length === 0) {
      dispatch(listRequestSent())
    }

    // api call to fetch list items
    apiGetList()
      .then((res) => {
        const items = res.data.result

        // update the localStorage list items
        setListLocalStorage(items)

        // update the list items in the store
        dispatch(updateList(items))
        dispatch(listRequestSuccess())
      })
  }
)


/**
 * Create new biller
 * 
 * @param {object} item 
 */
export const handleAddItem = (item) => (
  (dispatch, getState) => {
    dispatch(listRequestSent())
    
    apiCreateListItem()
      .then((res) => {
        const item = res.data.result

        // update the localStorage billers list
        const { items } = getState().list
        const itemsClone = items.slice()
        itemsClone.push(item)
        setListLocalStorage(itemsClone)

        // update the store
        dispatch(listAddItem(item))
        dispatch(ListRequestSuccess())
      })
  }
)


/**
 * Add the list to localStorage
 * 
 * @param {array} items 
 */
const setListLocalStorage = (items) => {
  const now = new Date().getTime()
  const listObject = { items }

  // set the expiration time for the localStorage item
  listObject.expiration = now + LOCAL_STORAGE_EXPIRATION_PERIOD

  // store the object in localStorage
  localStorage.setItem('list', JSON.stringify(list))
}


/**
 * Fetch the billers array from localStorage
 */
const getListLocalStorage = () => {
  // fetch the item from localStorage
  const listObject = JSON.parse(localStorage.getItem('list'))

  // get the current time
  const now = new Date().getTime()

  // check if localStorage item exists and it has not expired
  if (listObject && listObject.expiration > now) {
    return listObject.items
  }
  return []
}


/**
 * Set the initial state
 */
const initialState = {
  isRequesting: false,
  list: getListLocalStorage(),
}


export default function list(state = initialState, action) {
  switch (action.type) {
    case LIST_REQUEST_SENT:
      return {
        ...state,
        isRequesting: true,
      }
    case LIST_REQUEST_SUCCESS:
      return {
        ...state,
        isRequesting: false,
      }
    case UPDATE_LIST:
      return {
        ...state,
        items: action.list,
      }
    case LIST_ADD_ITEM:
      const items = state.items.slice()
      items.push(action.item)
      return {
        ...state,
        items,
      }
    default: {
      return state
    }
  }
}
const LIST_REQUEST_SENT = 'LIST_REQUEST_SENT'
const LIST_REQUEST_SUCCESS = 'LIST_REQUEST_SUCCESS'
const UPDATE_LIST = 'UPDATE_LIST'
const LIST_ADD_ITEM = 'LIST_ADD_ITEM'

const listRequestSent = () => ({
  type: LIST_REQUEST_SENT,
})

const listRequestSuccess = () => ({
  type: LIST_REQUEST_SUCCESS,
})

const updateList = list => ({
  type: UPDATE_LIST,
  list,
})

const listAddItem = (item) => ({
  type: LIST_ADD_ITEM,
  item,
})


/**
 * Handle fetching and storing of list items
 * 
 */
export const handleGetList = () => (
  (dispatch, getState) => {    
    dispatch(listRequestSent())
    // api call to fetch list items
    apiGetList()
      .then((res) => {
        const items = res.data.result

        // update the list items in the store
        dispatch(updateList(items))
        dispatch(listRequestSuccess())
      })
  }
)


/**
 * Create new biller
 * 
 * @param {object} item 
 */
export const handleAddItem = (item) => (
  (dispatch, getState) => {
    dispatch(listRequestSent())
    
    apiAddListItem()
      .then((res) => {
        const item = res.data.result

        // update the store
        dispatch(listAddItem(item))
        dispatch(ListRequestSuccess())
      })
  }
)


/**
 * Set the initial state
 */
const initialState = {
  isRequesting: false,
  list: [],
}


export default function list(state = initialState, action) {
  switch (action.type) {
    case LIST_REQUEST_SENT:
      return {
        ...state,
        isRequesting: true,
      }
    case LIST_REQUEST_SUCCESS:
      return {
        ...state,
        isRequesting: false,
      }
    case UPDATE_LIST:
      return {
        ...state,
        items: action.list,
      }
    case LIST_ADD_ITEM:
      const items = state.items.slice()
      items.push(action.item)
      return {
        ...state,
        items,
      }
    default: {
      return state
    }
  }
}