Amir Seighal
Amir Seighal

Reputation: 13

Infinite scroll in rails runs too many times

Trying to follow this tutorial on creating a infinite scroll with stimulus in rails. Here is what my view looks like :

<div data-controller="feed" data-action="scroll@window->feed#scroll" >
  <div data-feed-target="entries">
    <%= render(partial: "feed_posts")%>
  </div>
  <div data-feed-target="pagination"  hidden>
    <%== pagy_nav(@pagy)%>
  </div>
</div>

my rails controller:

  def feed 
    @new_post = Post.new
    @pagy, @posts = pagy(Post.where(user_id: current_user.all_following.pluck(:id)).or(Post.where(user_id: current_user.id)).order(created_at: :DESC), items: 5)
    respond_to do |format|
      format.html
      format.json{
        render json:{entries: render_to_string(partial: "feed_posts", formats: [:html]), pagination: view_context.pagy_nav(@pagy)}
      }
    end
  end

and finally my js controller:

export default class extends Controller {
    static targets = ["entries","pagination"]
    scroll(){
        let next_page = this.paginationTarget.querySelector("a[rel ='next']")
        if(next_page == null ){ return }
        let url = next_page.href
        var body = document.body,
            html = document.documentElement        
        var height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight)
        if(window.pageYOffset >= height - window.innerHeight - 100){

            console.log("loading")
            this.loadMore(url)
        }
    }
    loadMore(url){
        Rails.ajax({
            type: 'Get',
            url: url,
            dataType: 'json',
            success: (data) => {
                console.log(data)
                this.entriesTarget.insertAdjacentHTML('beforeend', data.entries);
                this.paginationTarget.innerHTML = data.pagination
            } 
        })
    }
}`

The code in charge for finding out if the bottom of the page is hit runs different amounts of times each time and adds too many results to the view.

How can i make it run once hopefully or have it check to see if the html being added, was added before or not..or maybe that's not a good idea.

Upvotes: 1

Views: 368

Answers (2)

therealrodk
therealrodk

Reputation: 434

The source for the tutorial you are using, GoRails, has a follow-up episode called Throttling Infinite Scroll Events in Javascript. If you are not a Pro member of that site, you will not be able to see the video, but there is a link to the source code on GitHub, which is freely available. It provides the following updated infinite_scroll_container.js:

import { Controller } from "stimulus"

export default class extends Controller {
  static targets = ["entries", "pagination"]

  scroll() {
    let next_page = this.paginationTarget.querySelector("a[rel='next']")
    if (next_page == null) { return }

    let url = next_page.href

    var body = document.body,
      html = document.documentElement

    var height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight)

    if (window.pageYOffset >= height - window.innerHeight) {
      this.loadMore(url)
    }
  }

  loadMore(url) {
    if (this.loading) { return }
    this.loading = true

    Rails.ajax({
      type: 'GET',
      url: url,
      dataType: 'json',
      success: (data) => {
        this.entriesTarget.insertAdjacentHTML('beforeend', data.entries)
        this.paginationTarget.innerHTML = data.pagination
        this.loading = false
      }
    })
  }
}

Upvotes: 1

eux
eux

Reputation: 3282

The technique for this called Throttle: Guarantee the execution of the function regularly, only once every X milliseconds.

Check out details on CSS-TRICKS - Debouncing and Throttling Explained Through Examples

The simple way to use throttle with stimulus is using _throttle function from lodash

_.throttle(func, [wait=0], [options={}])

Check out use case on Debounce and throttle in Stimulus, _.throttle inside a controller

Upvotes: 1

Related Questions