1

I have built an infinite scroll in reactjs where the data is being loaded from the same API as the user hit the bottom of the page. Now, the problem here is to get the data from multiple different API to fetch other data.So, what would be the right way to achieve this.Please help me out with this.Here is my code.

import React from "react";
import { Link } from 'react-router-dom';
import axios from 'axios';
const BUSY = {};

class InfiniteData extends React.Component{

constructor(props){
super(props);
this.state={
  olddata: [],
  newData: [],
  requestSent: false,
  scrollCounter:1
  }
  this.handleOnScroll = this.handleOnScroll.bind(this)
  this.buildData = this.buildData.bind(this)
}

componentDidMount(){
  window.addEventListener('scroll', this.handleOnScroll);
  this.doQuery(1).then(res=>
    this.setState({
     newData: this.state.newData.slice().concat(res),
    requestSent: false
  }))
}

componentWillUnmount() {
  window.removeEventListener('scroll', this.handleOnScroll);
}
queryActive = false;
doQuery(queryParam) {
  if(this.queryActive){
    return Promise.resolve(BUSY);
  }
  this.queryActive=true;
  switch(queryParam){
    case 1: return this.buildData(queryParam).then((res)=>res).catch(err=>{})
    break;
    case 2: return this.buildData(queryParam).then((res)=>res);
    break;
    case 3: return this.buildData(queryParam).then((res)=>res);
    break;
    case 4: return this.buildData(queryParam).then((res)=>res);
    break;
    default: return true;
  }

}

buildData(queryParam){
  //there is no paging in getting posts, won't it just get the same posts?
  return axios.get("https://jsonplaceholder.typicode.com/posts")
  .then( res=> {this.queryActive=false;return res.data;})
  .catch(err=>{
    this.queryActive=false;
    return Promise.reject(err);
  })
}


handleOnScroll(){
  var scrollCounter=0;
  var scrollTop = (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
  var scrollHeight = (document.documentElement && document.documentElement.scrollHeight) || document.body.scrollHeight;
  var clientHeight = document.documentElement.clientHeight || window.innerHeight;
  var scrolledToBottom = Math.ceil(scrollTop + clientHeight) >= scrollHeight;
  if (scrolledToBottom) {
    this.setState({
      scrollCounter: this.state.scrollCounter + Math.floor(scrolledToBottom)
    })
    if(this.state.scrollCounter<5){
      this.doQuery(this.state.scrollCounter).then(res=>
      (res===BUSY)
        ? false
        : this.setState({
            newData: this.state.newData.slice().concat(res)
          })
        )
        .catch(err=>this.setState({requestSent: false}))
        this.setState({requestSent: true});
    }else{
      return true
    }

  }
}

  render()
  {
    return (
    <div>
      <div className="data-container">
      <div  className="row ">
        {this.state.newData && this.state.newData.map((dat,i)=>
          <div key={i} className="col-lg-4">
              <div className="bordered">
                {dat.body}
              </div>
            </div>
        )}
          </div>
      </div>
    </div>
    );
  }
}

export default InfiniteData;
5
  • 1
    I bet the doQuery function should be responsible for fetching from various APIs; you can pass arguments to it to control which API to fetch from. I see this as a valid next little step towards your goal. Is there anything else that blocks or anyhow limits you in doing that? Side-note: Array#concat is non-destructive, so no need to precede it with Array#slice. Commented Feb 9, 2018 at 8:59
  • 1
    You can create an async/await function in doQuery that sends multiple requests to different APIs and attaches the result to state Commented Feb 9, 2018 at 9:47
  • 1
    Problem with the code is that you make the same request, there is no paging? The second problem is that you assume data resolves in the same order as the requests were made but this is not the case. You can make request 1,2,3 but get the results in order 1,3,2. You can handle this by not calling doQuery while doQuery is active. Commented Feb 9, 2018 at 10:13
  • @RishatMuhametshin do you mean to say, take the count of scrollbottom value and passing it as a argument to the doQuery() .And based on the parameter ,switch the axios get request? Please help me Commented Feb 9, 2018 at 11:58
  • I have updated my code.Please check it. Commented Feb 9, 2018 at 12:44

1 Answer 1

2

You could add a constant called BUSY to be used by doQuery as a return value for when it is already busy making a request:

import React from "react";
import { Link } from 'react-router-dom';
import axios from 'axios';
const BUSY = {};

You could make some changes to doQuery:

queryActive = false;
doQuery() {
  if(this.queryActive){
    return Promise.resolve(BUSY);
  }
  this.queryActive=true;
  //there is no paging in getting posts, won't it just get the same posts?
  return axios.get("https://jsonplaceholder.typicode.com/posts")
  .then( res=> {this.queryActive=false;return res.data;})
  .catch(err=>{
    //before you were only console.logging the error but that causes you
    //  to create a promise that doesn't reject but resolves with undefined
    //  when something goes wrong
    this.queryActive=false;
    console.warn(err);
    return Promise.reject(err);
  })
}

When you call doQuery make sure you ignore the result if the result if BUSY:

this.doQuery().then(res=>
  (res===BUSY)
    ? false
    : this.setState({
        newData: this.state.newData.slice().concat(res)
      })
)
.catch(err=>this.setState({requestSent: false}))
this.setState({requestSent: true});
Sign up to request clarification or add additional context in comments.

2 Comments

thanks, @HMR Could you please tell me how can I make various API requests in doquery to get different posts/data.
@karthik You're using json-server a mock json rest api that has paging So you just add _page and _limit something like this: jsonplaceholder.typicode.com/posts?_page=2&_limit=5 Best to ask for one more than you need so you know you need to get more. If the serer returns less than what you asked for then there are no more records to show.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.