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;
CodeZombie
  • 2,027
  • 3
  • 16
  • 30
  • 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`. – rishat Feb 09 '18 at 08:59
  • 1
    You can create an async/await function in doQuery that sends multiple requests to different APIs and attaches the result to state – Samuel Omole Feb 09 '18 at 09: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. – HMR Feb 09 '18 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 – CodeZombie Feb 09 '18 at 11:58
  • I have updated my code.Please check it. – CodeZombie Feb 09 '18 at 12:44

1 Answers1

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});
HMR
  • 37,593
  • 24
  • 91
  • 160
  • thanks, @HMR Could you please tell me how can I make various API requests in `doquery` to get different posts/data. – CodeZombie Feb 09 '18 at 11:42
  • @karthik You're using [json-server](https://github.com/typicode/json-server) a mock json rest api that has [paging](https://github.com/typicode/json-server#paginate) So you just add `_page` and `_limit` something like this: https://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. – HMR Feb 09 '18 at 12:24