Search code examples
javascriptreactjsreduxaxioswindow-scroll

Reactjs Redux: Infinite scroll with multiple different GET requests


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;

Solution

  • 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});