Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fetch data with React

Tags:

I am very new with React and I am trying to call the data from server with fetch api.

If I want to make a helper class to get the data like this one

import { Promise } from 'es6-promise';
import fetch from 'isomorphic-fetch';
let responseData = response => {
  return response;
};

let getData = (dataURL, headers) => {
  let result = {
    data: [],
    message: ''
  };
  if (dataURL === null || dataURL === undefined || dataURL === '') {
    result.message = 'URL is not correct';
    responseData(result);
  }
  fetch(dataURL, headers)
    .then(response =>{
      if (response.ok) {
        response.json().then(data => {
          result.data = data;
          result.message = response.statusText;
          responseData(result);
        });
      }else{
        result.message = 'Network response was not ok.';
        responseData(result);
      }
    })
    .catch(err => console.log(err));
};

exports.responseData = responseData;
exports.getData = getData;

and whenever the data is available, I will ask the React render the view, during the waiting time, it will show a loading icon.

I put the call inside componentDidMout but by the time it is executed, the result is always undefined.

componentDidMount() {
    var self = this;
    let result = util.getData('http://localhost:3001/menus');
    const { dispatch } = this.props;
    // dispatch(menuActions.fetchMenusIfNeeded());
    if (result !== null && result !== undefined) {
      if (this.isMounted()) {
        this.setState({
          items: result.data.results
        });
      }
    }
  }

In Angular, whenever the data is available, it will be updated accordingly but I really don't understand how to do it in React.

EDIT 1: Update the helper class:

import { Promise } from 'es6-promise';
import fetch from 'isomorphic-fetch';


let getData = (dataURL, headers) => {
  return fetch(dataURL, headers);
};

exports.getData = getData;

It will always return a Promise so I can chain handler for data in other components. The only problem that I need to handle the message return by reading Response object in my components.

In React component:

componentWillMount() {
    util.getData('http://localhost:3001/menus')
      .then(response =>{
        if (response.ok) {
          response.json().then(data => {
            console.log(data);
            this.setState({
              items: data.result
            });
          });
        }
      })
      .catch(err => console.log(err));
  }

But whenever it received the data, the render() function already finished, should I force it to update again?

like image 452
Raphaël VO Avatar asked Aug 05 '16 16:08

Raphaël VO


People also ask

What is fetch data in React?

Fetch data is a basic requirement of practically every React application. There are a variety of ways to fetch data in React, including the built-in Fetch API, Axios, async/await syntax, and others. We'll look at some of these methods in detail. React components can simply fetch their data.


2 Answers

result is not the data, it is a Promise, which can you use to access the data. You need to return the Promise from fetch and also need to return the data from your then within getData, and update your React code.

getData change

 return fetch(dataURL, headers)
  .then(response =>{
     if (response.ok) {
       response.json().then(data => {
         result.data = data;
         result.message = response.statusText;
         return responseData(result);
       });
     } else {
       result.message = 'Network response was not ok.';
       return responseData(result);
     }
  })
  .catch(err => console.log(err));

React change

componentDidMount() {
   var self = this;
   let result = util.getData('http://localhost:3001/menus');
   const { dispatch } = this.props;
   // dispatch(menuActions.fetchMenusIfNeeded());
   result.then((data) => {
      if (this.isMounted()) {
        this.setState({
          items: data.results
        });
      }
   });
 }
like image 145
Rob M. Avatar answered Sep 23 '22 16:09

Rob M.


You should not try to delay the rendering and/or mounting of the component until you get a response. Instead you should make use of callbacks to make sure that everything gets updated as you wish once the request is over.

You only need to modify the code for getData so that it executes a callback once the request is over. You have two options here.

  1. Make the getData function accept a third parameter callback and call this method once you handle the response. This would roughly look like the following:

let getData = (dataURL, headers, callback) => {
  let result = {
    data: [],
    message: ''
  };
  if (dataURL === null || dataURL === undefined || dataURL === '') {
    result.message = 'URL is not correct';
    responseData(result);
  }
  fetch(dataURL, headers)
    .then(response =>{
      if (response.ok) {
        response.json().then(data => {
          result.data = data;
          result.message = response.statusText;
          responseData(result);
        });
      }else{
        result.message = 'Network response was not ok.';
        responseData(result);
      }
      callback(result);
    })
    .catch(err => console.log(err));
};

Keep in mind that this is not how an actual implementation would look. In case of an exception the callback wouldn't be invoked and the calling code would never know what happened. But in essence, this is how it would work.

  1. Make the getData function return the Promise created by the fetch API. This is the one I'd use personally because it looks cleaner and I'd take Promises over callbacks any day of the week.

let getData = (dataURL, headers) => {
  let result = {
    data: [],
    message: ''
  };
  if (dataURL === null || dataURL === undefined || dataURL === '') {
    result.message = 'URL is not correct';
    responseData(result);
  }
  return fetch(dataURL, headers)
    .then(response =>{
      if (response.ok) {
        response.json().then(data => {
          result.data = data;
          result.message = response.statusText;
          responseData(result);
        });
      }else{
        result.message = 'Network response was not ok.';
        responseData(result);
      }
      return result;
    })
    .catch(err => console.log(err));
};

Just adding the return keyword would return the Promise and then you would be able to chain handlers to your heart's desire.

OK, how do we make use of this? It's actually pretty straightforward. You now have the ability to tell the getData method to do something once a Response is received (or an exception raised).

Just as an example, you could do this in your React component's componentWillMount method.

componentWillMount() {
    getData('http://example.com', new Headers())
        .then(function(result) {
            this.setState({ items: result });
        });
}

You can also control the loading panel by setting a flag in the state before the request and resetting it after the request (with callbacks of course).

like image 22
Can Ibanoglu Avatar answered Sep 23 '22 16:09

Can Ibanoglu