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.
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)
}
}
I have not tested this. I am just throwing my idea through code to get other experts idea on how I should handle such case for a more efficient technique without using other more external libraries.
1 Answer 1
This is one of the problems with async actions in redux-thunk. As you point out you have to create constants for "APARTMENT_FETCH_STATUS" and manually dispatch actions of this type before and after the request has been done.
The redux-promise-middleware library actually does all this for you. You simply create an action like so:
const foo = () => ({
type: 'FOO',
payload: new Promise()
});
and the library will take care of dispatching an action of type "FOO_PENDING", and then of type "FOO_FULFILLED" when the promise has resolved. With redux-promise-middleware your actions would become something like this:
const showResultOfApartment = () => ({
type: "FETCH_APARTMENT",
payload: axios.get("http://api.com/apartments").then(result => result.data)
})
In your reducer you would create handlers for "FETCH_APARTMENT_PENDING", "FETCH_APARTMENT_FULFILLED" and "FETCH_APARTMENT_REJECTED". In the case where the promise fulfills or rejects, you also get the result of the promise by accessing action.payload
. An example of what it could look like:
function apartmentReducer(state, action){
if(action.type === "FETCH_APARTMENT_PENDING"){
return Object.assign({}, state, { loading: true })
}
else if(action.type === "FETCH_APARTMENT_FULFILLED"){
return Object.assign({}, state, { loading: false, apartment: action.payload })
}
else if(action.type === "FETCH_APARTMENT_REJECTED"){
return Object.assign({}, state, { loading: false, error: action.payload })
}
return state;
}