How to fetch data from API for multiple items in R

2020-08-05 11:00发布

问题:

I am trying fetch data from API in my React JS component. Link to API. This API will populate the number count of each vehicle. The last argument 'audi' corresponds to vehicle make name. I need to fetch that data for 64 different vehicles and create dynamic list items (li) but don't know how to do that. Everything is working except fetchCount function.

Here is an example of vehicles data which is imported from '../shared/vehicle_make_and_models' there are total 64 care makes.

const veh_data = [
{ "alfa-romeo": ["145", "90", "Alfa 6", "Alfasud"] },
{ "aston-martin": ["15", "2-Litre", "AM Vantage", "Atom", "Cygnet", "DB2"]},
{ "audi": ["100", "200", "A1", "A2", "A3", "A4", "A5", "A6", "A7"] }
];

Here is my code:

import React, { Component } from 'react';
import { veh_data } from '../shared/vehicle_make_and_models'

class ShopByMake extends Component {
constructor(props) {
    super(props);

    this.fetchCount = this.fetchCount.bind(this);

    this.state = {
        veh_data: veh_data,
    };
}

fetchCount(make) {

    fetch('https://mysterious-journey-51969.herokuapp.com/api/vehicle-make-count/' + make)
            .then(response => {
                return response.json();
            })
            .then(data => {
            let firstKey = Object.keys(data)[0],
                count = data[firstKey];
            console.log('make count' + count);
            return count;
        })
        .catch(err => console.log(err)); 
  }

render() {

    const vehicles = this.state.veh_data.reduce((acc, veh) => {
        let make = Object.keys(veh)[0]

        return {
            makes: [
                ...acc.makes,
                <li className="mt-2"><a href="#"><img src={require('../assets/images/audi-logo.jpg')} className="img-fluid logos" /><span className="ml-4 list-text">{make} ({this.fetchCount(make)})</span></a></li>
            ]
        };
    }, { makes: [] });

    return (
        <div>
            <div className="headings-div text-center text-white mt-4 mt-lg-0"><h5 className="headings">Shop By Make</h5></div>
            <div className="mt-3" id="shopbymake">
                <ul className="list-unstyled">
                    {vehicles.makes}
                </ul>
            </div>
        </div>
    );
}
}

export default ShopByMake;

回答1:

  1. To perform the requests concurrently you could use Promise.all().

  2. When all requests are complete, filter out any null responses veh_data_with_count.filter(res=> res)

  3. Assign the veh_data_with_count property on the State Object using setState() to notify React of the changes and allow it to update the DOM if necessary.

import React, { Component } from "react";
import axios from "axios";
// import { veh_data } from '../shared/vehicle_make_and_models'
const veh_data = [
  { "alfa-romeo": ["145", "90", "Alfa 6", "Alfasud"] },
  { "aston-martin": ["15", "2-Litre", "AM Vantage", "Atom", "Cygnet", "DB2"] },
  { audi: ["100", "200", "A1", "A2", "A3", "A4", "A5", "A6", "A7"] }
];
class ShopByMake extends Component {
  constructor(props) {
    super(props);
    // this.fetchCount = this.fetchCount.bind(this);
    this.state = {
      veh_data_with_count: []
    };
  }
  componentDidMount() {
    Promise.all(
      veh_data.map(async car => {
        let make = Object.keys(car)[0];

        let res = await axios
          .get(
            "https://mysterious-journey-51969.herokuapp.com/api/vehicle-make-count/" +
              make.split('-').join('')
          )
          .catch(err => console.log(err));
        if (!res || !res.data) return null;
        let firstKey = Object.keys(res.data)[0],
            count = res.data[firstKey];

        return { make, count };
      })
    )
      .then(veh_data_with_count => {
        let removeFails = veh_data_with_count.filter(res=> res)
        this.setState({ veh_data_with_count: removeFails });
      })
      .catch(err => console.log(err));
  }

  render() {
    const vehicles = this.state.veh_data_with_count.map(
      ({ make, count }, i) => {
        return (
          <li key={i} className="mt-2">
            <a href="#">
              <img src="" className="img-fluid logos" />
              <span
                onClick={() => this.fetchCount(make)}
                className="ml-4 list-text"
              >
                {make} {count}
              </span>
            </a>
          </li>
        );
      }
    );

    return (
      <div>
        <div className="headings-div text-center text-white mt-4 mt-lg-0">
          <h5 className="headings">Shop By Make</h5>
        </div>
        <div className="mt-3" id="shopbymake">
          <ul className="list-unstyled">{vehicles}</ul>
        </div>
      </div>
    );
  }
}

export default ShopByMake;

https://codesandbox.io/s/lryq5lvn4q?moduleview=1



回答2:

You can create an API which will return counts for all the vehicle_make_name and then populating that data according to your need.