faraz
faraz

Reputation: 2733

redux saga ajax call - not working as expected

I have this redux saga code where everything works okay...until the promise, after that things start to go wrong here's the relevant code

const firstApiRequest = ()=>{
  return $.ajax({
    url: myUrl,// ,
    type:'POST',
    headers: {
        "Accept":"application/json",
        "Content-Type":"application/json",
    },
    data:JSON.stringify(bodyData),
       success:function(res){
          console.log(res);
          return res;
       }
    })
};



export function *startCheckout() {
    try {
        yield put(showLoading());
        const data = yield call(firstApiRequest);//const data ends
        yield put({type:FIRST_REQUEST_DONE,payload:data});           
    } catch (err) {
        yield put(firstRequestFail(err));
    }
}

export function *checkout() {
      yield takeEvery(SEND_FIRST_REQUEST, startCheckout);
}

The problem is that after the return res in firstApiRequest , I wanted to use the data to send the FIRST_REQUEST_DONE action , but what happens is that the flow goes to FIRST_REQUEST_FAIL and shows error as true. The problem is that the api response is coming back successfully and I am getting the data inside the error when the flow goes to FIRST_REQUEST_FAIL part of reducer and data shows up as error.

here's the code for reducer where flow goes to

case 'FIRST_REQUEST_FAIL':
      return {

        loading: false,
        error: true,
        errorMessage: action.err,
      };

instead of going to

case 'FIRST_REQUEST_DONE':
      return {
        id: action.id,
      };

so, what's wrong with the code here? why does it show error even after a succesful response from server?

Upvotes: 0

Views: 1064

Answers (2)

soupette
soupette

Reputation: 1280

Here is an approach to handle API request using redux-saga:

First create a request helper

import 'whatwg-fetch';

function parseJSON(response) {
 return response.json ? response.json() : response;
}

/**
 * Checks if a network request came back fine, and throws an error if 
   not
 *
 * @param  {object} response   A response from a network request
 *
 * @return {object|undefined} Returns either the response, or throws an 
 * error
*/
 function checkStatus(response, checkToken = true) {
   if (response.status >= 200 && response.status < 300) {
     return response;
  }

 return parseJSON(response).then(responseFormatted => {
   const error = new Error(response.statusText);
   error.response = response;
   error.response.payload = responseFormatted;
   throw error;
 });
}

/**
 * Requests a URL, returning a promise
 *
 * @param  {string} url       The URL we want to request
 * @param  {object} [options] The options we want to pass to "fetch"
 *
 * @return {object}           The response data
*/
export default function request(url, options = {}) {
// Set headers
  if (!options.headers) {
    options.headers = Object.assign({
     'Content-Type': 'application/json',
   }, options.headers, {});
  }

 // Stringify body object
 if (options && options.body) {
   options.body = JSON.stringify(options.body);
 }

 return fetch(url, options)
  .then(checkStatus)
  .then(parseJSON)
}

In your saga

import { call, fork, put, takeLatest } from 'redux-saga/effects';
import request from 'utils/request';

import { submitSuccess, submitError } from './actions'; // path 
to your actions.

import { SUBMIT } from './constants'; // The event you're listening

export function* submitData(action) {
  try {
    const response = yield call(request, 'your_url', { method: 'POST', body: action.body });
    yield put(submitSuccess(response));
  } catch(err) {
     yield put(submitError(response.payload.message);
  }
}

export function* defaultSaga() {
  yield fork(takeLatest, SUBMIT, submitData);
}

export default defaultSaga;

Reducer

const initialState = fromJS({
  submitSuccess: false,
  submitReponse: '',
  errorMessage: '',
});

 function fooReducer(state = initialState, action) {
   switch (action.type) {
     case SUBMIT_SUCCESS:
       return state
         .update('submitSuccess', () => true)
         .update('submitResponse', () => action.response);
     case SUBMIT_ERROR:
       return state.update('errorMessage', () => action.errorMessage);
     //...
   }
  }

With this structure you should be able to catch your success and you error when you're making your request.

Upvotes: 0

Diogo Sgrillo
Diogo Sgrillo

Reputation: 2701

You shouldn't be defining the success in your api request. $.ajax will return a promise on its own:

const firstApiRequest = () => (
  $.ajax({
    url: myUrl,// ,
    type:'POST',
    headers:{
        "Accept":"application/json",
        "Content-Type":"application/json",
    },
    data:JSON.stringify(bodyData),
}));

Also, why are you using jQuery for making the API requests? I'd suggest using axios or fetch

Upvotes: 1

Related Questions