Skip to content

Issues with inverted/top infinite load. #70

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
lanmaster53 opened this issue Aug 16, 2023 · 10 comments
Open

Issues with inverted/top infinite load. #70

lanmaster53 opened this issue Aug 16, 2023 · 10 comments

Comments

@lanmaster53
Copy link
Contributor

lanmaster53 commented Aug 16, 2023

Below is the code I've put together to do inverted infinite scrolling. I want the behavior to behave exactly like your demo, however, I keep running into the following issues and cannot figure out why mine is behaving different than yours.

  1. The initial load won't start at the bottom of the scroll element unless the slot attribute is bound. It's weird. If I override a slot with a template alone, it starts at the top of the scroll element, which is wrong. If I override a slot with the attribute alone, it starts at the bottom of the scroll element, which it should. I prefer to override with a template, so in order to make it work I have to include the template and the attribute binding with an empty object as shown in the code below. This doesn't seem right.
  2. The transition is not smooth. Whenever the infinite handler triggers, it automatically scrolls all the way to the top of all the new messages. This also isn't like your demo.

Do you see what I am doing wrong to cause these issues? Thanks!

Edit: This is partial code. I stripped out a bunch of other stuff that is unrelated to the infinite scroll stuff.

const template = `
<div class="flex-row messaging">
    <div class="flex-grow flex-column flex-justify-end messages">
        <div id="message-container" class="message-container">
            <infinite-loading target="#message-container" :slots="{}" :top="true" :identifier="infiniteId" :firstload="false" @infinite="infiniteHandler">
                <template #complete>
                    <span></span>
                </template>
            </infinite-loading>
            <div v-for="message in messages" v-bind:key="message.id" v-bind:message="message" class="flex-row message">
                ... stuff ...
            </div>
        </div>
    </div>
</div>
`

export default {
    name: "Messages",
    template,
    setup () {
        const messages = ref([]);
        const cursor = ref(null);

        function getMessages($state) {
            var query = "";
            if (cursor.value) {
                query = "?cursor="+cursor.value;
            };
            fetchWrapper.get(`${API_BASE_URL}/messages${query}`)
            .then(json => {
                cursor.value = json.cursor;
                // prepend messages with the older messages
                messages.value.unshift(...json.messages);
                if (json.next) {
                    $state.loaded();
                } else {
                    $state.complete()
                };
            })
            .catch(error => appStore.createToast(error));
        };

        function infiniteHandler($state) {
            getMessages($state);
        };

        return {
            messages,
            infiniteId,
            infiniteHandler,
        };
    },
};
@lanmaster53
Copy link
Contributor Author

lanmaster53 commented Aug 17, 2023

After a lot of testing and reviewing the code for this component, I think I found out what's causing my issues.

The $state.complete method doesn't calculate the scrollTop like the $state.loaded method does, which causes the component to jump to the last bit of data on the final load. In your demo, there is an unending stream of data, so the user never gets to the last request where the complete method is called. I found this behavior because I am testing with a small amount of data. I tried to fix the problem by calling the loaded method just before complete, but the isVisible function triggers a loop of loads until the target element is filled, and if there isn't enough data to fill the target element, then it keeps trying to load data. The issues that result are dependent on the API providing the data, but it isn't a viable workaround.

Is this something that needs to be fixed, or am I missing something?

@lanmaster53
Copy link
Contributor Author

Here's my temporary fix, which proves out the issue. The first line in my infinite handler stores the target element's previous height in the local state. Just like your component does. Then, when the complete condition is met, I follow the call to the complete method with the following code. The final transition is now smooth.

nextTick(() => {
    scrollContainer.value.scrollTop = scrollContainer.value.scrollHeight - prevHeight;
});

@oumoussa98
Copy link
Owner

oumoussa98 commented Aug 17, 2023

I'v tried to reproduce this issue but it works just fine for me
If you can manage to provide a fully functional example, it would help a lot.
You can start from this one on StackBlitz

@lanmaster53
Copy link
Contributor Author

I'v tried to reproduce this issue but it works just fine for me If you can manage to provide a fully functional example, it would help a lot. You can start from this one on StackBlitz

Your example doesn't accurately represent the problem because line 16 of App.vue will never be true in your app. The API is never ending, so it will always return 10. However, if you had a dataset of 38 items, then on the last request for data that line would be true and you would experience the issue I am reporting.

@lanmaster53
Copy link
Contributor Author

lanmaster53 commented Aug 18, 2023

Use the following App.vue in your stackblitz to replicate the issue. Notice how the final load causes a jump to the top.

<script setup>
import { ref } from 'vue';
import InfiniteLoading from 'v3-infinite-loading';
import 'v3-infinite-loading/lib/style.css';

let comments = ref([]);
let page = 0;
const load = async ($state) => {
  console.log('loading more...');
  page++;
  try {
    const response = await fetch(
      'https://jsonplaceholder.typicode.com/posts?_limit=30&_page=' + page
    );
    const json = await response.json();
    comments.value.unshift(...json);
    if (json.length < 30) {
      $state.complete();
    } else {
      $state.loaded();
    }
  } catch (error) {
    $state.error();
  }
};
</script>
<template>
  <div class="top-results">
    <InfiniteLoading @infinite="load" :top="true" target=".top-results" />
    <div v-for="comment in comments" :key="comment.id" class="result">
      <div>{{ comment.title }}</div>
      <div>{{ comment.id }}</div>
    </div>
  </div>
</template>
<style>
.top-results {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  height: 500px;
  width: 380px;
  overflow-y: scroll;
  margin: 0 auto;
  border-radius: 10px;
  padding: 10px;
}
.result {
  font-weight: 300;
  width: 80%;
  padding: 20px;
  text-align: center;
  background: #eceef0;
  border-radius: 10px;
}
</style>

@oumoussa98
Copy link
Owner

Oh okay, now i see the issue.
Thanks.

@lanmaster53
Copy link
Contributor Author

Oh okay, now i see the issue. Thanks.

Do you plan to fix it?

@lanmaster53
Copy link
Contributor Author

Any progress on this issue?

@ilieBeracha
Copy link

Any updates on the issue? i got the same problem

@Sakthi002
Copy link

Same issue!!! Any update please

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants