davidb
davidb

Reputation: 1603

React JS fetch data from multiple API function

I was tasked to create a front-end for a poc. I am not a front end developer but I chose to use React JS. There is only one page fetching data from multiple API endpoints. API endpoints return a simple json object. I managed to get that to work however my code is ugly af and I want to create a function to handle all of that but I can't seem to get it right. Here's my code

export default class Dashboard extends React.Component {

constructor(props) {
    super(props);
    this.state = {
      group1: [],
      group2: [],
      group3: [],
      isLoaded: false,
    }
  }

componentDidMount() {
    const group1_url = "http://localhost/api/1"
    const group2_url = "http://localhost/api/2"
    const group3_url = "http://localhost/api/3"

    fetch(group1_url)
      .then(res => res.json())
      .then(json => {
        this.setState({
          group1: json,
        })
      });
    fetch(group2_url)
      .then(res => res.json())
      .then(json => {
        this.setState({
          group2: json,
        })
      });
    fetch(group3_url)
      .then(res => res.json())
      .then(json => {
        this.setState({
          group3: json,
        })
      });
  }

I am trying to create a function like this:

 function fetch_data(url, state) {
    fetch(url)
      .then(res => res.json())
      .then(json => {
        this.setState({
          state: json,
        })
      });
  }

var group1 = fetch_data(group1_url, group1);

So far no joy. How can I create a function to fetch data and set a state in js? Alternatively how can I make my code look better? Or is there something else I should use/look into?

Upvotes: 1

Views: 1814

Answers (3)

Mario
Mario

Reputation: 4998

You could try Promise.all

Promise.all takes an array of promises (it technically can be any iterable, but is usually an array) and returns a new promise.

const points = [
    "http://localhost/api/1",
    "http://localhost/api/2",
    "http://localhost/api/3",
];

const responses = await Promise.all(points.map((point) => fetch(point)));
const data = await Promise.all(responses.map((response) => response.json()));
const [group1, group2, group3] = data;

this.setState({
    group1,
    group2,
    group3,
});

Just remember to wrap this logic in an async function

Upvotes: 2

CertainPerformance
CertainPerformance

Reputation: 370729

Pass a string as the second parameter, and use a computed property:

function fetch_data(url, state) {
    fetch(url)
      .then(res => res.json())
      .then(json => {
        this.setState({
          [state]: json,
        })
      });
  }

fetch_data(group1_url, 'group1');

I'd also highly recommend catching errors - possible unhandled rejections should be avoided whenever possible.

You might want to use Promise.all to wait for all groups to load:

const dataSources = {
    group1: 'http://localhost/api/1',
    group2: 'http://localhost/api/2',
    group3: 'http://localhost/api/3',
};
Promise.all(
    Object.entries(dataSources).map(([propertyName, url]) => (
        fetch(url)
            .then(res => res.json())
            .then((result) => {
                this.setState({
                    [propertyName]: result
                })
            })
    ))
)
    .then(() => {
        this.setState({ isLoaded: true })
    })
    .catch((error) => {
        // handle errors
    })

(also note that your json argument is not a JSON - JSON is only a format that exists with strings. Something that has been deserialized is just a plain object or array. Better to call it something less misleading, like result as I did)

Upvotes: 2

Yadab
Yadab

Reputation: 1883

You can do something like this.

function fetch_data(url, state) {
fetch(url)
  .then(res => res.json())
  .then(json => {
    this.setState({
      [state]: json,
    })
  });
}

var group1 = fetch_data(group1_url, 'group1');

Upvotes: 1

Related Questions