React - retrieving values from multiple API requests and displaying it in render function

I'm very new to react. Here I'm trying to do multiple API requests and the flow is as follows,

Firstly I'm calling this.getAllJobsAPI(); which retrieves a JSON string array. Then during each callback i'm calling another API using this.getLastBuildAPI(job); function which returns an id for corresponding string value. During callback of this API I'm calling this.getAndroidLintResultAPI(id, job); which returns var numberOfHighPriorityWarnings integer value in it's callback.

I need to display these in a Pie chart (VictoryPie) with corresponding string job name retrieved from this.getAllJobsAPI() and it's numberOfHighPriorityWarnings value retrieved from this.getAndroidLintResultAPI(id, job);.

Appreciate your insight to get this working.

Code

      import React, { Component } from 'react';
      import logo from './logo.svg';
      import './App.css';
      import { VictoryPie } from 'victory';


      class App extends Component {

        getAllJobsAPI(){
          var headers = new Headers();
          headers.append('Authorization', 'Basic YWRtaW46YWRtaW4=');

          fetch('http://localhost:8080/api/json', {headers: headers})
          .then((result) => {
            // Get the result
            // If we want text, call result.text()
            return result.json();
          }).then((jsonResult) => {
            // get all jobs 
            var jobs = [];

            Object.keys(jsonResult.jobs).forEach(key => {
              jobs.push(jsonResult.jobs[key].name);

              var job = jsonResult.jobs[key].name;
              console.log(job);

              this.getLastBuildAPI(job);   

          });

          console.log(jsonResult);

          })
        }

        getLastBuildAPI(job){
          var headers = new Headers();
          headers.append('Authorization', 'Basic YWRtaW46YWRtaW4=');

          fetch('http://localhost:8080/job/'+ job +'/lastBuild/api/json', {headers: headers})
          .then((result) => {
            // Get the result
            // If we want text, call result.text()
            return result.json();
          }).then((jsonResult) => {
            // get the build id and build status 
            var id = jsonResult.id;
            var status = jsonResult.result;

            console.log('id '+ id); 
            console.log('status '+ status);

            this.getAndroidLintResultAPI(id, job);

            console.log(jsonResult);
          })
        }

        getAndroidLintResultAPI(id, job){
          var headers = new Headers();
          headers.append('Authorization', 'Basic YWRtaW46YWRtaW4=');



          fetch('http://localhost:8080/job/'+ job +'/'+ id +'/androidLintResult/api/json', {headers: headers})
          .then((result) => {
            // Get the result
            // If we want text, call result.text()
            return result.json();
          }).then((jsonResult) => {
            // get lint results 
            var numberOfHighPriorityWarnings = jsonResult.numberOfHighPriorityWarnings;
            console.log('numberOfHighPriorityWarnings ' + numberOfHighPriorityWarnings);

            console.log(jsonResult);
          })
        }

        render() {
          this.getAllJobsAPI();

          return (
            <div className="App">
              <header className="App-header">
                <img src={logo} className="App-logo" alt="logo" />
                <h1 className="App-title">Build Results</h1>
              </header>

              <VictoryPie 
                data={[
                  { x: "job name 1", y: numberOfHighPriorityWarnings for the job name 1},
                  { x: "job name 2", y: numberOfHighPriorityWarnings for the job name 2}
                ]}
              />

            </div>
          );
        }
      }

      export default App;

Appreciate if you can point me how to get these data in VictoryPie of the render function.

Thanks,

1 answer

  • answered 2018-01-14 10:23 mersocarlin

    I strongly recommend the usage of Promises. Not to mention you can chain all your calls and make your code even easier to understand.

    However, remember that the more api calls you make, the more it will take until you get the final result into your component. You are calling getLastBuildAPI only to get each job's id, for example. Can't you return each job's id in getAllJobsAPI and remove the extra getLastBuildAPI?

    Nevertheless, I made an example so you get the idea of how Promise is going to help you out.

    I'm also using setTimeout to simulate asynchronous calls.

    /* api.js (Remove this whole logic from your component, it doesn't need to know how you fetch each job) */
    
    const generateRandomInt = () => parseInt(Math.random() * 100, 10)
    const generateRandomJobs = () => [...new Array(3).keys()].map(key => `Job ${key}`)
    
    function getAndroidLintResultAPI(id, job) {
      return new Promise(resolve =>
        setTimeout(() => resolve({
          id,
          name: job,
          highPriorityWarnings: generateRandomInt(),
        }), 1000)
      )
    }
    
    function getLastBuildAPI(job) {
      return new Promise(resolve =>
        setTimeout(() => {
          const jobId = generateRandomInt()
          resolve({
            id: jobId,
            name: job,
          })
        }, 1000)
      )
    }
    
    function getAllJobsAPI() {
      return new Promise(resolve =>
        setTimeout(() => {
          const jobs = generateRandomJobs()
          resolve(jobs)
        }, 1000)
      )
    }
    
    // if possibe, export only this method to your component
    function getJobList() {
      return getAllJobsAPI()
      .then(jobs => Promise.all(jobs.map(getLastBuildAPI)))
      .then(jobs => Promise.all(jobs.map(job => getAndroidLintResultAPI(job.id, job.name))))
    }
    
    /* Start your component in a new file */
    class App extends React.Component {
      constructor() {
        super()
    
        this.state = {
          loading: true,
          jobs: []
        }
    
        
      }
    
      componentDidMount() {
        getJobList().then(jobs => this.setState({ loading: false, jobs }))
      }
    
      render() {
        const { loading, jobs } = this.state
        return (
          <div>
            { loading
              ? <div>Fetching jobs...</div>
              : this.state.jobs.map(job =>
                  <div key={job.id}>{`${job.name}: ${job.highPriorityWarnings}`}</div>
                )
            }
          </div>
        )
      }
    }
    
    ReactDOM.render(
      <App />,
      document.getElementById('root')
    )
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    <div id="root"></div>