Search code examples
javascriptreactjsflickr

React JS - stop infinite scroll at the end of array


I have this simple react app, where I fetch the Flickr public feed. So, I can scroll to the end of the page and new content is going to show. So I would like to scroll until there is nothing else new, and the app stops trying to load more content, because it has reached the last item of the list, which is not happening if I try to scroll (you can see that on the loading message). How can I fix this?

Check the code below:

import React, { Component } from "react";
import ReactDOM from "react-dom";
import $ from "jquery";

import PhotoListItem from "./photoListItem";

import "./photoApp.css";

export default class PhotoApp extends Component {
  constructor(props) {
    super(props);

    this.state = {
      photoList: [],
      searchTerm: "cyanotype",
      items: 8,
      loadingState: false,
      loadingMessage: ""
    };
  }

  componentDidMount() {
    this.getPhotoList();
    this.onInfiniteScroll();
  }

  /* get data from Flickr public feed */
  getPhotoList = () => {
    const flickrApiPoint =
      "https://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?&tags=" +
      this.state.searchTerm;

    try {
      $.ajax({
        url: flickrApiPoint,
        dataType: "jsonp",
        data: { format: "json" },
        success: function(data) {
          this.setState({ photoList: data.items });
        }.bind(this)
      });
    } catch (err) {
      console.log(err);
    }
  };

  /* code for infinite scroll */
  onInfiniteScroll = () => {
    this.refs.iScroll.addEventListener("scroll", () => {
      if (
        this.refs.iScroll.scrollTop + this.refs.iScroll.clientHeight >=
        this.refs.iScroll.scrollHeight - 20
      ) {
        this.loadMoreItems();
      }
    });
  };

  /* code for infinite scroll */
  loadMoreItems = () => {
    if (this.state.loadingState) {
      return;
    }

    this.setState({
      loadingState: true,
      loadingMessage: "Loading photos..."
    });
    setTimeout(() => {
      this.setState(prevState => ({
        items: prevState.items + 8,
        loadingState: false,
        loadingMessage: "No more photos to show."
      }));
    }, 1000);
  };

  render() {
    console.log(this.state.photoList)
    return (
      <div className="appContainer" ref="iScroll">
        <div className="appHeader">
          <h1 className="headerTitle">
            Welcome to Flickr Alternative Photography Feed!
          </h1>
        </div>

        <div className="gridContainer">
          {this.state.photoList
            .slice(0, this.state.items)
            .map((photo, index) => {
              const author = photo.author.split(/"/)[1];
              const authorLink = photo.description.split(/"/)[1];
              const description = photo.description.split(/"/)[13];
              return (
                <PhotoListItem
                  key={index}
                  url={photo.media.m}
                  photoLink={photo.link}
                  title={photo.title}
                  author={author}
                  authorLink={authorLink}
                  description={description}
                  tags={photo.tags}
                />
              );
            })}
        </div>

        <React.Fragment>
          {this.state.loadingState ? (
            <p className="loading">{this.state.loadingMessage}</p>
          ) : (
            <p className="loading">{this.state.loadingMessage}</p>
          )}
        </React.Fragment>
      </div>
    );
  }
}

LIVE EXAMPLE HERE

Thank you!


Solution

  • You could check if the item that you've loaded exceeds your items in your ajax request

    
      /* code for infinite scroll */
      loadMoreItems = () => {
        // hasMore = data.items.length (you may want to rename this more appropriately)
        if (this.state.loadingState || (this.state.items > this.state.hasMore)) {
          // Do not load if there's no more items
          return;
        }
        ...