1

How to display json files content one after another when clicked.

I have 3 json files**(biology.json,chemistry.json and physics.json)** that I want to show its content one after another on showmore button click.

biology.json

[
      { "id" : 1, "course" : "Bio101"},
      { "id" : 2, "course" : "Bio102" }

    ]

chemistry.json

[
      { "id" : 3, "course" : "Chem101"},
      { "id" : 4, "course" : "Chem102" }

    ]

physics.json

[
      { "id" : 5, "course" : "Physics101"}

    ]

I also have file_count.json that stores all the 3 json files above including all total amount of data in each file.

file_count.json

[
      { "id" : 1, "filename" : "biology.json", "content_total" : 2},
      { "id" : 2, "filename" : "chemistry.json", "content_total" : 2 },
      { "id" : 3, "filename" : "physics.json", "content_total" : 1 }

    ]

what am trying to achieve: I want to load content of each of the 3 files (biology.json,chemistry.json and physics.json) one after another when button is clicked. based on their filesname or any other means in file_count.json. For instance, on page loading fetched biology.json data, on next showmore button click fetch Chemistry.json data, next fetch physics.json data and so on.

With code below, I can successfully fetch biology.json data. how do I fetch chemistry.json and physics.json data
one after another when showmore button is clicked. Thanks

Here is the coding so far

<!DOCTYPE html>
<html>
   <head>

   </head>
   <body>

<script src="build/react.min.js"></script>
<script src="build/react-dom.min.js"></script>
<script src="build/browser.min.js"></script>
<script src="build/jquery.min.js"></script>


<div id="app"></div>

<script type="text/babel">
class Application extends React.Component {

  constructor(props) {
    super(props)

    this.state = {
     file_count : [],    
     limit : 1,
     data:[]

    };
    this.showMore = this.showMore.bind(this);
  }


  componentDidMount() {

//load filecount.json
    $.ajax({
      type: "POST",
      url: "http://localhost/axios/axios-master/dist/file_count.json",
      cache: false,
      crossdomain:true,
      success: function(file_count) {
         this.setState({file_count: file_count});
       }.bind(this),
       error: function(jqXHR) {
         console.log(jqXHR);
       }.bind(this)
    });


//load biology.json 
    $.ajax({
      type: "POST",
      url: "http://localhost/axios/axios-master/dist/biology.json",
      cache: false,
      crossdomain:true,
      success: function(data) {
         this.setState({data: data});
       }.bind(this),
       error: function(jqXHR) {
         console.log(jqXHR);
       }.bind(this)
    });
  }



  showMore() {
   // load chemistry.json file on click
  // load  physics.json file on click
  }

 get finished() {
   if (this.state.limit == 0) {
   // you can also set state for loading image
      return <li key={"done"}>No More json files to Load. Content 
       finished</li>;
    }
    return null;
  }


  render() {
    return <div className="container">
     {this.finished}

      <div className="row">
        <h3>List of records</h3>
        <ul>

          {this.state.data.map((f, i) => 
            <li key={i}>{f.id} - {f.course}</li>)}
        </ul>
      </div>
      <p>
        <a className="btn btn-primary" onClick={this.showMore}>Show 
         more</a>.
      </p>
    </div>;
  }
}

ReactDOM.render(<Application />, document.getElementById('app'));


</script>

<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>


   </body>
</html>

Updated part of the code based on solution provided by @Rahul and @t3__rry

@t3__rry thanks for your response. I have Updated the code below to reflect your solutions but it throws error unexpected token. expected ";" at this line of code pointing to the currentIndex colon

currentIndex: prevState.currentIndex + 1,

here is the code

import React, { Component } from 'react';
//import axios from 'axios';


export default class Paginate extends Component {
constructor(props) {
    super(props)

    this.state = {
     file_count : [],    
     data:[],
currentIndex: 0,

    };
    this.showMore = this.showMore.bind(this);


  }


async fetchData(filename) {
  const response = await fetch(`http://localhost/${filename}`);
  const data = await response.json()

  return data;
}





async componentDidMount() {
  const fileCountJSON = await this.fetchData('http://localhost/filecount.json');

  if (fileCountJSON) {
    // will return an array with your file names e.g. ['biology.json', 'chemistry.json'] ...
    const filenames = fileCountJSON.map(file => file.filename);
    this.setState(() => ({
      fileCount: filenames,
    }));

    // then fetch the firstData in the list of fileCount
    const { currentIndex, fileCount } = this.state;
    const initialData = await this.fetchData(fileCount[currentIndex]);

    // and set the state based on what you get
    this.setState((prevState) => {
      data: initialData,
      // and increment currentIndex to be able to trigger the next load
      currentIndex: prevState.currentIndex + 1,
    });
  }
}

// in show more do what you did previously
showMore() {
  const { currentIndex, fileCount } = this.state;

  // duplicated code here, we could abstract it into a function
  if (currentIndex < fileCount.length - 1) { // checks if we still have fileCount items
      const data = await this.fetchData(fileCount[currentIndex]);

      this.setState((prevState) => ({
        data,
        currentIndex: prevState.currentIndex + 1,
      }));
    return;
  }
}

    render() {


 currentIndex < fileCount.length - 1 ? (
    this.state.data.map((f, i) => <li key={f.id}>{f.id} - {f.course}</li>)
  ) : (
    <li>No More json files to Load. Content finished</li>
  )

    }
}

@Rahul thanks for responding. I have updated the code to reflect your solution. When I run it, no error nothing showed.

<!DOCTYPE html>
<html>
   <head>

   </head>
   <body>

<script src="build/react.min.js"></script>
<script src="build/react-dom.min.js"></script>
<script src="build/browser.min.js"></script>
<script src="build/jquery.min.js"></script>


<div id="app"></div>

<script type="text/babel">
class Application extends React.Component {

  constructor(props) {
    super(props)

    this.state = {
     file_count : [],    
     limit : 1,
     data:[],
index:0

    };
    this.showMore = this.showMore.bind(this);
  }


  componentDidMount() {

//load filecount.json
    $.ajax({
      type: "POST",
      url: "http://localhost/axios/axios-master/dist/file_count.json",
      cache: false,
      crossdomain:true,
      success: function(file_count) {
         this.setState({file_count: file_count});
       }.bind(this),
       error: function(jqXHR) {
         console.log(jqXHR);
       }.bind(this)
    });



 const { file_count, index } = this.state;
if(index>file_count.length-1){
console.log("No more file to load")
return;
}
const { filename } = file_Count[index];
//Use file name to make api call

 $.ajax({
      type: "POST",
      url: "http://localhost/axios/axios-master/dist/filename",
      cache: false,
      crossdomain:true,
      success: function(data) {
         //this.setState({data: data});

this.setState({data:data, index: this.state.index+1});

       }.bind(this),
       error: function(jqXHR) {
         console.log(jqXHR);
       }.bind(this)
    });

  }


  showMore() {

 const { file_count, index } = this.state;
if(index>file_count.length-1){
console.log("No more file to load")
return;
}
const { filename } = file_Count[index];
//Use file name to make api call

 $.ajax({
      type: "POST",
      url: "http://localhost/axios/axios-master/dist/filename",
      cache: false,
      crossdomain:true,
      success: function(data) {
         //this.setState({data: data});

this.setState({data:data, index: this.state.index+1});

       }.bind(this),
       error: function(jqXHR) {
         console.log(jqXHR);
       }.bind(this)
    });

  }



  render() {
    return <div className="container">


      <div className="row">
        <h3>List of records</h3>
        <ul>

          {this.state.data.map((f, i) => 
            <li key={i}>{f.id} - {f.course}</li>)}
        </ul>
      </div>
      <p>
        <a className="btn btn-primary" onClick={this.showMore}>Show 
         more</a>.
      </p>
    </div>;
  }
}

ReactDOM.render(<Application />, document.getElementById('app'));


</script>

<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>


   </body>
</html>
Nancy Moore
  • 2,322
  • 2
  • 21
  • 38

2 Answers2

1

So, you have to make an API call on show more click. You can take file name from file_count to make API call for that file, but You will have to track for which file you should make an API call. To keep that track one way you can do that is after making API call for any particular file delete its name from file_count and when click on show more take 0th index element from file_count, make api call for that. Or you can create a new state variable to track index of file to make API call. for example at first, that index will be zero and after making API call for biology update-index to 1, so that next API call will be for chemistry and so on.

import React, { Component } from 'react';
//import axios from 'axios';


export default class Paginate extends Component {
constructor(props) {
    super(props)

    this.state = {
     file_count : [],    
     data:[],
     currentIndex: 0,

    };
    this.showMore = this.showMore.bind(this);


  }


async fetchData(filename) {
  const response = await fetch(`http://localhost/${filename}`);
  const data = await response.json()

  return data;
}





async componentDidMount() {
  const fileCountJSON = await 
   this.fetchData('filecount.json');

  if (fileCountJSON) {
    // will return an array with your file names e.g. ['biology.json', 'chemistry.json'] ...
    const filenames = fileCountJSON.map(file => file.filename);
    this.setState(() => ({
      fileCount: filenames,
    }));

    // then fetch the firstData in the list of fileCount
    const { currentIndex, fileCount } = this.state;
    const initialData = await this.fetchData(fileCount[currentIndex]);

    // and set the state based on what you get
    this.setState((prevState) => {
      data: [...prevState.data, initialData],
      // and increment currentIndex to be able to trigger the next load
      currentIndex: prevState.currentIndex + 1,
    });
  }
}

// in show more do what you did previously
showMore() {
  const { currentIndex, fileCount } = this.state;

  // duplicated code here, we could abstract it into a function
  if (currentIndex < fileCount.length - 1) { // checks if we still have fileCount items
      const data = await this.fetchData(fileCount[currentIndex]);

      this.setState((prevState) => ({
        data: [...prevState.data , data],
        currentIndex: prevState.currentIndex + 1,
      }));
    return;
  }
}

     render() {
    return <div className="container">


      <div className="row">
        <h3>List of records</h3>
        <ul>

          {this.state.data.map((f, i) => 
            <li key={i}>{f.id} - {f.course}</li>)}
        </ul>
      </div>
      <p>
        <a className="btn btn-primary" onClick={this.showMore}>Show 
         more</a>.
      </p>
    </div>;
  }

}
Rahul
  • 108
  • 1
  • 8
  • Thanks @Rahul for responding. please can you give a little code sample so that I can get more insight. between thanks – Nancy Moore Mar 11 '20 at 09:22
  • I have written few lines of code but another code in @t3__rry answer is much clear and we are doing the same thing. So you can checkout that code too. – Rahul Mar 11 '20 at 09:50
  • thanks for responding again. I have implemented your solution but still have issues. please can you see the updated part of my post above for issues am having. thank you – Nancy Moore Mar 11 '20 at 17:33
  • @NancyMooree I have update my code by making minor fixes in t3__rry codes. Hope it helps you. – Rahul Mar 12 '20 at 18:50
  • For the cors error checkout this:https://stackoverflow.com/questions/31276220/cors-header-access-control-allow-origin-missing – Rahul Mar 12 '20 at 18:53
  • Thanks @Rahul for all your help. however, it now throws error unexpected token at this line of code `currentIndex: prevState.currentIndex + 1,` as can be seen in the screenshot url link below https://imgur.com/a/Olc2zrV – Nancy Moore Mar 12 '20 at 22:40
1

you could create an async fetchData function (pause execution) which takes a filename as a parameter: In your state, add a currentIndex or whatever prop;

this.state = {
  currentIndex: 0,
  ...
};
async function fetchData(filename) {
  const response = await fetch(`http://localhost/axios/axios-master/dist/${filename}`);
  const data = await response.json()

  return data;
}

And in componentDidMount fetch file_count:

componentDidMount() {
  const fileCountJSON = await this.fetchData('filecount.json');

  if (fileCountJSON) {
    // will return an array with your file names e.g. ['biology.json', 'chemistry.json'] ...
    const filenames = fileCountJSON.map(file => file.filename);
    this.setState(() => ({
      fileCount: filenames,
    }));

    // then fetch the firstData in the list of fileCount
    const { currentIndex, fileCount } = this.state;
    const initialData = await this.fetchData(fileCount[currentIndex]);

    // and set the state based on what you get
    this.setState((prevState) => {
      data: initialData,
      // and increment currentIndex to be able to trigger the next load
      currentIndex: prevState.currentIndex + 1,
    });
  }
}

// in show more do what you did previously
showMore() {
  const { currentIndex, fileCount } = this.state;

  // duplicated code here, we could abstract it into a function
  if (currentIndex < fileCount.length - 1) { // checks if we still have fileCount items
      const data = await this.fetchData(fileCount[currentIndex]);

      this.setState((prevState) => ({
        data,
        currentIndex: prevState.currentIndex + 1,
      }));
    return;
  }
}

Then in your render function you could use the same comparison there's above:

{
  currentIndex < fileCount.length - 1 ? (
    this.state.data.map((f, i) => <li key={f.id}>{f.id} - {f.course}</li>)
  ) : (
    <li>No More json files to Load. Content finished</li>
  )
}
t3__rry
  • 2,817
  • 2
  • 23
  • 38
  • Here are links to async await docs if you want to take a look https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await https://dev.to/shoupn/javascript-fetch-api-and-using-asyncawait-47mp – t3__rry Mar 11 '20 at 09:34
  • thanks for responding . I have implemented your solution but still have issues. please can you see the updated part of my post above for issues am having. thank you – Nancy Moore Mar 11 '20 at 17:34
  • Thanks @t3__rry for all your help. however, it now throws error **unexpected token** at this line of code `currentIndex: prevState.currentIndex + 1,` as can be seen in the screenshot url link below https://imgur.com/a/Olc2zrV – Nancy Moore Mar 12 '20 at 22:41
  • Hi @NancyMooree from you screenshot you for forgot an opening and a closing parenthesis. ```this.setState((prevState) => ({ .... }));``` it's an implicit return statement – t3__rry Mar 13 '20 at 08:44
  • Glad it has helped you @NancyMooree take care ;) – t3__rry Mar 16 '20 at 11:11