1

I have some data services that I want to add to my app. I want those services will be accessible to use in few components and to avoid the need to rewrite "fetch functions" in each component. After some research about getting data from server, and asynchronous call behavior (like here ) I realize that there is not easy way to return data from those call, and the only way is to do the dependent execution is inside the callbacks.

So I get this solution- This work well, but my question is: Is it problematic solution ?

DataServices:

 static getDataTry2  (path, obj)  {

     fetch(path)
     .then(myResponse => myResponse.json())
     .then(arrivedData => {
       console.log("the arrived data ", arrivedData)
       obj.setState({
         try1 : arrivedData
       });

     });
   }


my other component:

componentDidMount() {

        DataServices.getDataTry2("ws/getEvents", this);


    }

lingar
  • 477
  • 1
  • 7
  • 25
  • 2
    If you create the separate service and import into current component. It is not problematic. You can fetch the service under the componentDidMount method.It will work – Shivani Sonagara Jul 02 '19 at 10:31

3 Answers3

4

Make a request abstraction is a standard solution but for separation of concerns, your service should be able to make requests but it should return a promise that will be managed by the caller.

Usually the request abstraction manages also 400/401 errors (for refresh tokens/logout) but is agnostic about the logic of the caller.

This is how look like a common abstraction:

 /**
 * Parses the JSON returned by a network request
 *
 * @param  {object} response A response from a network request
 *
 * @return {object}          The parsed JSON from the request
 */
function parseJSON(response) {
  if (response.status === 204 || response.status === 205) {
    return null;
  }
  return response.json();
}

/**
 * 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) {
  if (response.status >= 200 && response.status < 300) {
    return response;
  }

  const error = new Error(response.statusText);
  error.response = response;
  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) {
  return fetch(url, options)
    .then(checkStatus)
    .then(parseJSON);
}

Then a service will look like this:

import request from 'shared/lib/request';
import { API } from 'shared/constants';

const create = (content) => request(
  {
    url: API.MY_ENDPOINT,
    method: 'POST',
    data: content,
  });

const get = (id) => request(
  {
    url: `${API.MY_ENDPOINT}/${id}`,
    method: 'GET',
  });

const put = (content) => request(
  {
    url: `${API.MY_ENDPOINT}/${content.id}`,
    method: 'PUT',
    data: content,
  });

const MyEndpointService = {
  create,
  get,
  put,
};

export default MyEndpointService;

Usage, wherever you want (also outside react scope):

import MyEndpointService from '/API/MyEndpointService'

MyEndpointService.create(payload)
    .then((data) => {
    // code
    })
    .catch((errors) => {
    // error code
    });
Mosè Raguzzini
  • 15,399
  • 1
  • 31
  • 43
0

I would rather use a state manager like redux to centralize data fetching across different components.

Specifically, you create actions performing the actual data fetching, so you can call them inside the ComponentDidMount hooks.

Take a look at this article: https://dev.to/markusclaus/fetching-data-from-an-api-using-reactredux-55ao

Michele
  • 751
  • 1
  • 8
  • 16
  • how the state manager is related to fetch requests ? A fetch request can be retained to local state or do not be retained at all. – Mosè Raguzzini Jul 02 '19 at 10:36
  • Thanks, indeed in this situation I am starting to get the need for Redux – lingar Jul 02 '19 at 10:40
  • 2
    So @MosèRaguzzini Redux not supposed to handle this issues ? – lingar Jul 02 '19 at 10:43
  • You simply place your fetch requests inside redux actions, so they can be triggered from anywhere in your application. Of course the response is stored in the global state, but data fetching code gets shared in a clean way. – Michele Jul 02 '19 at 10:45
  • 2
    @lingar Redux manage global state. You DO NOT NEED it in order to make a request. Additionaly, to manage requests inside actions you need anyway an abstraction. – Mosè Raguzzini Jul 02 '19 at 10:46
  • 1
    "I want those services will be accessible to use in few components and to avoid the need to rewrite "fetch functions"". Redux is A solution to this, not THE solution. – Michele Jul 02 '19 at 10:48
  • Copy that @MosèRaguzzini – lingar Jul 02 '19 at 10:49
  • 1
    No one I know would reccomend a global store to solve a http fetch service problem. If I need a wheel no one will suggest to build a truck to get the wheel. – Mosè Raguzzini Jul 02 '19 at 10:53
  • 1
    It is something that happens quite often instead, it helps so much to centralize network calls using Redux as the application scales up. Anyway, @lingar now you know more than one approach to the problem :) – Michele Jul 02 '19 at 10:57
  • 2
    @Michele this depends on the application he's building. If it manages ephimeral data or it is a micro app Redux may be an overhead. Also the use of thunks can be problematic when you have to write tests – Mosè Raguzzini Jul 02 '19 at 12:00
  • 2
    I agree. The latter can be solved with redux-saga, check it out. – Michele Jul 02 '19 at 12:02
  • Indeed @Michele a productive discussion :) . Anyway I understand U agree that with Redux also a fetch service is needed to be defined ? – lingar Jul 03 '19 at 15:48
0

A solution with typescript,

interface HTTP_METHODS {
      POST: string;
      PUT: string;
      GET: string;
      DELETE: string;
    }

const HTTP_METHOD: HTTP_METHODS = {
  POST: 'POST',
  PUT: 'PUT',
  GET: 'GET',
  DELETE: 'DELETE',
};

const createHeaders = (headers: any) => {
  let commonHeaders = {
    ...headers,
    'Content-Type': 'application/json',
  };

  return commonHeaders;
};

const create = async (method: string, url: string, body?: any, headers?: any) => {
  return await fetch(url, {
    method: method,
    headers: createHeaders(headers),
    body: JSON.stringify(body),
  });
};

const API = {
  create,
  HTTP_METHOD,
};

export default API;

Then you can call,

import API from '../../services/api';
const resultRawTest = await API.create(API.HTTP_METHOD.GET,"https://reqres.in/api/unknown");

const resultTest = await resultRawTest.json();

console.log(" resultTestresultTest ", resultTest);