PeachScript / vue-infinite-loading

An infinite scroll plugin for Vue.js.
https://peachscript.github.io/vue-infinite-loading/
MIT License
2.66k stars 366 forks source link

Infinite handler does not stop being triggered resulting in fetching the data partially until no more data is provided #328

Open pbilka46 opened 1 year ago

pbilka46 commented 1 year ago

Version

2.4.5

Vue.js version

2.6.11

Description

I'm trying to use Vue infinite loading with a native, body scroll of the browser as presented in this demo.

The problem is that the data is loaded partially, but also recursively - what I mean is that on page load, even when I don't try to scroll, the infinite handler is triggered and it fetches the next data, but should not.

The afterwards logic calls $state.complete() after it fetches all the items, so it stops there.

Reproduction Link

          <infinite-loading
            v-if="showInfinite"
            force-use-infinite-wrapper=".__xd"
            @infinite="handleInfiniteLoader"
          >
            <div slot="no-more"></div>
            <div slot="no-results"></div>
          </infinite-loading>

The handler:

     async handleInfiniteLoader($state: {
      loaded: () => any;
      complete: () => any;
    }) {
      const results = await this.load();
      console.log("results", results);
      if (results.length > 0) {
        $state.loaded();
      } else {
        $state.complete();
      }
    },

BTW it loads 25 items at first, to avoid triggering infinite loader right after the first load. Meaning I'm filling the whole page with items; then only scrolling down should trigger the handler.

Also, what I tried - I thought the infinite loader is visible at first screen paint and that's why it's recursively triggering the handler. So I tried to load some items on component mounted and wrap infinite-loader inside v-if statement, only to make infinite-loader being rendered after the page is filled with 25 results (meaning infinite loader would render for the first time at the very end of the page, being not visible in the viewport).

Steps to reproduce

I cannot make a demo to reproduce the issue today and I think the description I gave is fair enough. I will try to make a demo tomorrow if needed.

What is Expected?

The infinite handler should be fired when scrolling actually makes infinite-loading component be visible in the viewport.

What is actually happening?

The infinite handler is triggered recursively until all the data is fetched, even without doing any "scroll" at all.

chris-basebone commented 1 year ago

Hi @pbilka46 , have you found any solution/workaround to this yet? Having the same issue in a Nuxt.js app, and considering just building it myself at this stage.

Sakthi002 commented 1 year ago

+1

evanlegamer commented 1 year ago

+1

pbilka46 commented 1 year ago

@chris-basebone @Sakthi002 @evanlegamer I'm not sure if it helps you but I solved it somehow. And I can only guess what happened.

So when the bug in my case was still appearing, I had a Vue component (let's call it Data.vue and inside:

          <infinite-loading
            @infinite="handleInfiniteLoader"
          >
            <div slot="no-more"></div>
            <div slot="no-results"></div>
          </infinite-loading>

Inside the same component Data.vue, under methods key I had a method that was handling the @infinite event emit:

    async handleInfiniteLoader($state: {
     loaded: () => any;
     complete: () => any;
   }) {
     const results = await this.load();
     console.log("results", results);
     if (results.length > 0) {
       $state.loaded();
     } else {
       $state.complete();
     }
   },

The bug was the same as described in this issues first post.

What I changed so that it magically started working: tl;dr - I extracted helper method to separate file. I made something like helpers/infinite.ts and inside:

export interface InfiniteLoaderState {
  loaded: () => void;
  complete: () => void;
}

export const infiniteHandler = async <T>(
  state: InfiniteLoaderState,
  loadResults: () => Promise<T[]>
) => {
  const results = await loadResults();
  if (results.length > 0) {
    state.loaded();
  } else {
    state.complete();
  }
};

I imported both the interface and infiniteHandler function in Data.vue and used it as follows: Template:

          <infinite-loading @infinite="handler">
            <div slot="no-more"></div>
            <div slot="no-results"></div>
          </infinite-loading>

Methods:

    async handle(state: InfiniteLoaderState) {
      await infiniteHandler<MyDataType>(
        state,
        this.load
      );
    },

And it started to working as expected... I'm really not sure why this is a fix; I guess this may be the problem with Vue, when the handler (before my fix) was directly under the methods key and Vue decided somehow to re-render (I'm just guessing).

Inside the component method on the other hand, when I passed there a separate async function helper that does the work, it started working as expected.

Hope this helps someone, please get back to me if this solves the issue for you @chris-basebone @Sakthi002 @evanlegamer