-1

I am creating a react app and I want to show only show 21 number of elements at a time and keep the rest in idle and show another 21 elements when scroll to bottom of page.

I have my component that gets the products and insets an add every 20 products. I want to only show 21 elements at a time (20 products and 1 add) while keeping the rest idle to load another 21 elements when they scroll down. Is there a way to pre-emptively fetch the next batch of results in advance and keep them idle till they need displaying (when user reaches end of the last bach of results).

this is my component

        import React from "react";
import { Loading } from "./LoadingComponent";

const axios = require('axios');

class Products extends React.Component {


  constructor(props){
        super(props);
        this.state = {
      displayedList : [],
      indexDisplayed : 0,
      isLoading: false,
      error: null
    }
    }



  _injectAdsInList = list => {
    // an array of your ads

    this.setState({ isLoading: false });
    const AdsList = ['111','2222'];

    if (Array.isArray(list)) {
      const insertAdAt = 21;
      const productsAndAdsList = list.map((item, index) => {
        // not sure about this calculation
        // but this should be done here
        if (index % insertAdAt === 0 && index !== 0) {
          // its the 20th item insert an ad
          return AdsList[0];
        }

        // otherwise insert the item
        return item;
      });

      this.setState({ productsAndAdsList });
    }
  };





  _getProducts = () => {

    this.setState({ isLoading: true });
    // make the fetch call
    // use axios (https://www.npmjs.com/package/axios)
    // for making http requests
    axios.get('http://localhost:3000/products').then(list => this._injectAdsInList(list.data))
      .catch(error => this.setState({ error, isLoading: false }));
  };

  _addToDisplay = ()=> {

    let displayedList = this.state.displayedList;
    let indexDisplayed = this.state.displayedList;
    let productsAndAdsList = this.state.displayedList;
        for(let i = indexDisplayed + 1; i <= indexDisplayed + 21; i++ ){
            displayedList.push(productsAndAdsList[i]);
        }

        this.setState({
            indexDisplayed  : this.state.indexDisplayed+21, 
            displayedList : displayedList
        });
    }


  _sortPrice = () => {
    const { productsAndAdsList } = this.state;
    // instead of mutating the origin array
    // create a new one, and update the state
    const sortedProducts = productsAndAdsList.sort((a, b) => a.price - b.price);
    this.setState({ productsAndAdsList: sortedProducts });
  };

  _sortSize = () => {
    const { productsAndAdsList } = this.state;
    // instead of mutating the origin array
    // create a new one, and update the state
    const sortedProducts = productsAndAdsList.sort((a, b) => a.size - b.size);
    this.setState({ productsAndAdsList: sortedProducts });
  };

  _sortId = () => {
    const { productsAndAdsList } = this.state;  
    const sortedProducts =  productsAndAdsList.sort((a, b) => {
  if (a.id < b.id)
    return -1;
  if (a.id > b.id)
    return 1;
  return 0;
});
    this.setState({ productsAndAdsList: sortedProducts });
  }

   _timeAgo = (prevDate) => {
        const diff = Number(new Date()) - prevDate;
        const minute = 60 * 1000;
        const hour = minute * 60;
        const day = hour * 24;
        const month = day * 30;
        const year = day * 365;
        switch (true) {
            case diff < minute:
                const seconds = Math.round(diff / 1000);
                 return `${seconds} ${seconds > 1 ? 'seconds' : 'second'} ago`
            case diff < hour:
                return Math.round(diff / minute) + ' minutes ago';
            case diff < day:
                return Math.round(diff / hour) + ' hours ago';
            case diff < month:
                return Math.round(diff / day) + ' days ago';
            case diff < year:
                return Math.round(diff / month) + ' months ago';
            case diff > year:
                return Math.round(diff / year) + ' years ago';
            default:
                return "";
        }
    };

  componentDidMount() {
    // I personally prefer not to make any
    // http calls directly in componentDidMount
    // instead, creating a new function makes sense
    this._getProducts();
  }

  render() {

    const { isLoading, hasError } = this.state;

console.log(this._addToDisplay());

    // show spinner
    if (!hasError && isLoading) {
      return <Loading />;
    }

    // show error
    if (hasError && !isLoading) {
      return <p>error.message</p>;
    }

    return (
      <div>
       <div className="row">
            <div className="col-12">
                <button onClick={this._sortPrice}>sort by price lower to higher</button>
                <button onClick={this._sortSize}>sort by size small to big</button>
                <button onClick={this._sortId}>sort by Id</button>  
            </div>  
        </div>
        { this.state.displayedList.map(item => { return (
                        <div className="row">
              <div className="col-4">
                <p> Price: ${(item.price / 100).toFixed(2)}</p>
              </div>

              <div className="col-4">
                <p style={{ fontSize: `${item.size}px` }}> {item.face}</p>
              </div>
            <div className="col-3">
                  <p>Published: {this._timeAgo(new Date(item.date).getTime())}</p>
                </div>
            </div>
            ) } )

                }

          <button onClick={this._addToDisplay()}>Load 21 MORE</button>

       <div className="row">
            <div className="col-12">
                <p>END OF CATALOG</p>  
            </div>  
        </div>
      </div>
    );
  }
}

export default Products;
Natasha
  • 93
  • 2
  • 9
  • There are a handful of react infinite scroll libraries, not sure how they work or if that's what you're looking for. – TKoL Nov 20 '19 at 16:16
  • Yeah I have to do it with javascript I am not supposed to use a library. Thats why I am a bit lost – Natasha Nov 20 '19 at 16:27
  • So your problem is showing 21 elements and when you scroll down adding 21 more that's it ? Do you want to fetch ALL the data and display them little by little or do you want to fetch the first 21 elements and when you scroll down, fetch the 21 nexts ? – Maxime Girou Nov 20 '19 at 16:38
  • I want to fetch ALL the data and display little by little. – Natasha Nov 20 '19 at 16:41

1 Answers1

1

Store the result in a variable and each time you want to display more call the addToDisplay function.

In the render, use the displayedList. I made a simplier version of your code to explain my words so you can implement the solutions the way you want in your code

I assume that you will find a way to trigger addToDisplay as your issue was not about how to trigger a function when you scroll down.

class MyComponent extends React.Component{

    constructor(props){
        super(props);
        this.state = {
            list : [],
            displayedList : [],
            indexDisplayed : 0
        }
    }

    componentDidMount(){
      this.loadData();
    }

    loadData = ()=> {

        axios.get('http://localhost:3000/products').then(list => {
            this.setState({list : list});
            this.addToDisplay();
        });
    }


    addToDisplay = ()=> {
        let displayedList = this.state.displayedList;
        for(let i = indexDisplayed + 1; i <= indexDisplayed + 21; i++ ){
            displayedList.push(list[i]);
        }

        this.setState({
            indexDisplayed  : this.state.indexDisplayed+21, 
            displayedList : displayedList
        });
    }

    render(){
        return(
            <div>
                // use this.state.displayedList to display your elements
                {
                    this.state.displayedList.map(item => {
                        // the way you want to display your item
                    })
                }

                <button onClick={this.addToDisplay}>Load 21 MORE</button>
            </div>
        )

    }


}
Maxime Girou
  • 1,511
  • 2
  • 16
  • 32
  • I have created a function and added to my code but I get undefined if I do a console.log(_adToDisplay) this is my function: _addToDisplay = () => { const { productsAndAdsList } = this.state; let displayedList = this.state.displayedList; let indexDisplayed = 0; for(let i = indexDisplayed; i <= indexDisplayed + 21; i++ ){ displayedList.push(productsAndAdsList[i]); } this.setState({ indexDisplayed : this.state.indexDisplayed+21, displayedList : displayedList }); } – Natasha Nov 21 '19 at 02:46
  • 1
    @Natasha In react you must call your function with this.before, like this._addToDisplay(). Futhermore, your function is call ` _addToDisplay` but in your console.log it says _adToDisplay=> you must put this._addToDisplay() – Maxime Girou Nov 21 '19 at 09:05
  • @MaxineGirou I copied your code to try and work with this and add the functions I want to this but I am getting an error saying Line 52:7: Expected an assignment or function call and instead saw an expression no-unused-expressions. Line 52 is this.state.displayedList.map(item => { I have added some divs into it I have updated my question with how I edited your code – Natasha Nov 28 '19 at 15:12
  • 1
    It's because you did not return the divs in your map. put a return with the brackets. like `this.state.displayedList.map(item => { return (
    ....
    ) } );`. IF this solve your question, mark the answer has resolve ;)
    – Maxime Girou Nov 29 '19 at 08:33
  • I am getting this error: TypeError: Cannot read property '0' of undefined refering to this line | displayedList.push(productsAndAdsList[i]); Here is a stackblitz to my components https://react-pjdhjc.stackblitz.io – Natasha Nov 29 '19 at 15:45
  • I updated my code in my question now I am getting an error saying: TypeError: Cannot read property 'price' of undefined. My displayedList has nothing in it I dont understand why – Natasha Nov 29 '19 at 16:14
  • This is not the topic anymore and purposes of SO is not to debug your code. It you make a minimum search you will find : https://stackoverflow.com/questions/32317154/react-uncaught-typeerror-cannot-read-property-setstate-of-undefined – Maxime Girou Nov 29 '19 at 16:42
  • I get this type error because my displayedList array has nothing in it and I dont understand why I doesnt – Natasha Nov 29 '19 at 19:12
  • You must debug your code. Are you sure that your array is empty ? if you log `this.state.displayedList` or `productsAndAdsList` what it says ? – Maxime Girou Nov 29 '19 at 23:28
  • I get an array with this content Array(2) 0: undefined 1: undefined length: 2 – Natasha Dec 02 '19 at 14:20