1
votes

I am developing an application where there are lots of async actions. I wanted to go with redux-saga but most have insisted to continue with redux-thunk. In redux-thunk, inside each action we have to work with async operation using then, dispatch, catch, etc. This makes looks actions so messy and lots of code will be repeated. I wanted to create a generic dataLoader for the use of redux-thunk and axios but could not consider for both post(might be token or not) and get option.

Here is my attempt:

export class Company {
/**
* Generic api data loader
*/
static dataLoader(apiUri, onSuccess, onError, data, ...actionArguments) {
  const requestURL = `${API_BASE}${apiuri}`;
  try {
    let options;
    if (data !== undefined) {
      // if we have data to post
      options = {
      method: 'POST',
      url: requestURL,
      body: JSON.stringify(data),
      headers: {
         'Content-Type': 'application/json',
         'X-Requested-With': 'XMLHttpRequest',
      },
      };
    }
  }
  return function(dispatch) {
    axios(options)
    .then(response => {
      dispatch({
        type: onSucess,
        payload: response.data
      });
    })
    .catch(error => {
      dispatch({ type: onError, payload: err});
    });
  }
}

static get(apiUri, onSuccess, onError, ...actionArguments) {
    return this.dataLoader(apiUri, onSuccess, onError, undefined, ...actionArguments);
  }

  /*
   * Shorthand POST function
   */
  static post(apiUri, onSuccess, onError, data, ...actionArguments) {
    return this.dataLoader(apiUri, onSuccess, onError, data, ...actionArguments);
  }

}

I want to convert the following code to further this one:

export function showResultofApartment() {
  return (dispatch) => {
    dispatch({ type: 'APARTMENT_FETCH_START' });
    const token = localStorage.getItem('token');
    return axios.get(`${API_URL}/newoffers/apartment/`)
    .then((response) => {
      console.log('response apart', response.data);
        dispatch({ type: 'APARTMENT_FETCH_SUCCESS', payload: response.data });
    })
    .catch((err) => {
      dispatch({ type: 'APARTMENT_FETCH_FAILURE', payload: err });
    });
  };
}

to such or more efficient than this:

export function showResultofApartment() {
  return(dispatch) => {
    dispatch({ type: APARTMENT_FETCH_START });
    const token = localStorage.getItem('token');
    return Company.get('/apartments', APARTMENT_FETCH_SUCCESS, APARTMENT_FETCH_ERROR);
    // if post then Company.post('/apartment', APARTMENT_POST_SUCCESS, APARTMENT_POST_ERROR, data)
  }
}

This way it is considering only post request(if data !== undefined). How should i handle for both get and post efficiently?

1
Not to create more confusion, I feel redux-saga is more elegant for you in case you have multiple synchronous or async axios request to handle. In case you are interested, I can post a pattern which we followed in our company. - Nagaraj Tantri
I would be greatful to you if i know the pattern you use but at the same time i want this with redux-thunk for this project. This will be the last time we are using redux-thunk. To continue the project, i have to stick with thunk. Can you help me with the thunk, regarding generic dataLoader, please? - Serenity

1 Answers

1
votes

Okay, why don't you handle it like this:

Company.js

import { merge } from 'lodash';
import axios from 'axios';

function getHeaders() {
  return {
   'Content-Type': 'application/json'
 };
}

export class Company {

static callAPI(endpoint, extendedOptions, onSuccess, onError) {
  const initalHttpData = {
    method: 'GET', // By default it's GET in case you didnt specify anything
    headers: getHeaders(),
    url: `${API_BASE}${endpoint}`
  };

  // merge takes care of replacing or adding the specific key's provided via the extendedOptions
  const options = merge(initalHttpData, extendedOptions);

  // Fire the request for the prepared options.
  let request = axios(options);

  // The request once fired, needs it's success handler and error handler.
  return function(dispatch) {
    request
    .then(response => {
      dispatch({
        type: onSucess,
        payload: response.data
      });
    })
    .catch(error => {
      dispatch({ type: onError, payload: err});
    });
  }
};
}

Then we can use actions to specifically pass things to this api util:

GET API call:

// GET Action
export function showResultofApartment() {
  return (dispatch) => {
    dispatch({ type: APARTMENT_FETCH_START });
    const token = localStorage.getItem('token');

    // FOR GET API
    return Company.callApi('/apartments', {}, APARTMENT_FETCH_SUCCESS, APARTMENT_FETCH_ERROR);
  }
}

POST API call:

// POST Action
export function showResultOfAppartmentPost() {
  return (dispatch) => {
    dispatch({ type: APARTMENT_FETCH_START });
    const token = localStorage.getItem('token');

    // This will merge, essentially replace the method=GET once it gets called.
    const extendedOptions = {
      method: 'POST',
      body: JSON.stringify(data),
      headers: {
         'X-Requested-With': 'XMLHttpRequest',
      }
    }

    // FOR GET API
    return Company.callApi('/apartments', extendedOptions, APARTMENT_FETCH_SUCCESS, APARTMENT_FETCH_ERROR);
}

Thus, giving the action, to define it's own set of API body or requests.