8

During implementing login feature with React, Redux, isomorphic-fetch, ES6 Babel.

Questions

I do not know how to properly combine promises after the checkstatus promise in order to get parsed JSON data from my server.
what am I doing wrong here?

also, do I need to replace isomorphic-fetch package with other more convenient one?
any suggestion for other package is welcome!

loginAction.js

import * as API from '../middleware/api';
import * as ActionTypes from '../actionTypes/authActionTypes';
import 'isomorphic-fetch';

function encodeCredentials(id, pwd) {
  return btoa(`${id}{GS}${pwd}`);
}

function checkStatus(response) {
  if (response.status >= 200 && response.status < 300) {
    response;
  } else {
    const error = new Error(response.statusText);
    error.response = response;
    throw error;
  }
}

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

export function loginFailure(error) {
  return { error, type: ActionTypes.LOGIN_FAILURE };
}

export function loginSuccess(response) {
  return dispatch => {
    dispatch({ response, type: ActionTypes.LOGIN_SUCCESS });
  };
}

export function loginRequest(id, pwd) {
  return {
    type: ActionTypes.LOGIN_REQUEST,
    command: 'login',
    lang: 'en',
    str: encodeCredentials(id, pwd),
    ip: '',
    device_id: '',
    install_ver: '',
  };
}


export function login(id, pwd) {
  const credentials = loginRequest(id, pwd);

  return dispatch => {
    fetch(`${API.ROOT_PATH}${API.END_POINT.LOGIN}`, {
      method: 'post',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(credentials),
    })
    .then(checkStatus)
    .then(parseJSON)
    .then(data => {
      console.log(`parsed data ${data}`);
      dispatch(loginSuccess(data));
    })
    .catch(error => {
      console.log(`request failed ${error}`);
    });
  };

}
seoyoochan
  • 822
  • 3
  • 14
  • 28

2 Answers2

9

In my projects usually, I have a helper function fetchJSON that does all utility logic, such as JSON parsing and status check.

Here it is:

import fetch from 'isomorphic-fetch';

function checkStatus(response) {
  if(response.ok) {
    return response;
  } else {
    const error = new Error(response.statusText);
    error.response = response;
    throw error;
  }
}

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

export default function enhancedFetch(url, options) {
  options.headers = Object.assign({
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  }, options.headers);
  if(typeof options.body !== 'string') {
    options.body = JSON.stringify(options.body);
  }
  return fetch(url, options)
    .then(checkStatus)
    .then(parseJSON);
}

Then you can use it in actions:

import fetchJSON from '../utils/fetchJSON'; // this is the enhanced method from utilities

export function login(id, pwd) {
    const credentials = loginRequest(id, pwd);

    return dispatch => {
       fetchJSON(`${API.ROOT_PATH}${API.END_POINT.LOGIN}`, {
           method: 'post',
           body: credentials
       }).then(data => {
           console.log(`parsed data ${data}`);
           dispatch(loginSuccess(data));
       }).catch(error => {
           console.log(`request failed ${error}`);
       });
   };
}

It helps you to keep actions code clean from some boilerplate code. In big projects with tons of similar fetch calls it is a really must-have thing.

just-boris
  • 9,468
  • 5
  • 48
  • 84
  • Oh wow! I really appreciate that. This is really useful best practices i have ever seen so far! – seoyoochan Jan 14 '16 at 10:40
  • 1
    Object assign modifying the original object spit a warning, so i passed {} for the first argument. – seoyoochan Jan 14 '16 at 15:26
  • I see that on error you set error.response = response What if an API server sends an Error object according to the JSON API specification? http://jsonapi.org/format/#error-objects How would you parse the error object then? – bhtabor Jun 06 '16 at 18:01
  • @Biruk you can find a solution in related question: http://stackoverflow.com/questions/29473426/fetch-reject-promise-with-json-error-object – just-boris Jun 07 '16 at 13:08
  • @Biruk also, if you have to deal with JSON API in the typical way, you may use special library for it, for example https://github.com/mzabriskie/axios – just-boris Jun 07 '16 at 13:10
  • @just-boris I use isomorphic-fetch but the related question is very helpful. Thanks a lot! – bhtabor Jun 09 '16 at 15:50
  • Nice code snippet. Just one comment, Object.assign is not supported on IE, so that code will break on Windows's users. Instead use some polyfill to make a copy without using Object.assign like this https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign – sandino Jan 07 '17 at 01:18
4

You're doing it right, you just forgot return in checkstatus; you should return the response such that the next promise in the chain can consume it.

Also, it seems that checkstatus is synchronous operation, so it's no need to chain it by .then (although, it's OK if you like it that way), you can write:

fetch(...)
.then(response=>{
   checkStatus(response)
   return response.json()
})
.then(data=>{
   dispatch(loginSuccess(data))
})
.catch(...)

I see no reason to get rid of isomorphic-fetch for now - it seems that it does its job.

evandrix
  • 6,041
  • 4
  • 27
  • 38
Tomas Kulich
  • 14,388
  • 4
  • 30
  • 35