Skip to content

Instantly share code, notes, and snippets.

@wobsoriano
Last active November 23, 2022 17:13
Show Gist options
  • Save wobsoriano/41a01290a9967c7f39077fe5c4a70436 to your computer and use it in GitHub Desktop.
Save wobsoriano/41a01290a9967c7f39077fe5c4a70436 to your computer and use it in GitHub Desktop.
Vue 3 Infinite Scroll Component
<script setup lang="ts">
import { ref } from 'vue';
import InfiniteScroll from '~/components/InfiniteScroll.vue';
import SkeletonPostItem from '~/components/SkeletonPostItem.vue';
interface State {
loaded: () => void;
complete: () => void;
}
const posts = ref([]);
const hasNextPage = ref(false);
const loadMore = async () => {
// your load more function
// set posts and hasNextPage values here
};
const handleIntersect = async ($state: State) => {
if (hasNextPage) {
await loadMore();
$state.loaded();
} else {
$state.complete();
}
};
</script>
<template>
<div v-for="post in posts" :key="post.id">
<!-- Post content -->
</div>
<InfiniteScroll v-if="posts.length" @intersect="handleIntersect">
<template #placeholder>
<SkeletonPostItem v-for="n in 5" :key="n" />
</template>
</InfiniteScroll>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue';
const props = withDefaults(defineProps<{
options?: IntersectionObserverInit
}>(), {
threshold: 0,
rootMargin: '0px',
root: null,
});
const emit = defineEmits<{(e: 'intersect', payload: {
loaded: () => void
complete: () => void
}): void
}>();
let observer: IntersectionObserver;
const root = ref<HTMLDivElement>();
const isIntersecting = ref(false);
const isComplete = ref(false);
onMounted(() => {
observer = new IntersectionObserver(([entry]) => {
if (entry && entry.isIntersecting && root.value) {
isIntersecting.value = true;
observer.unobserve(root.value);
emit('intersect', {
loaded() {
isIntersecting.value = false;
observer.observe(root.value!);
},
complete() {
observer?.disconnect();
isIntersecting.value = false;
isComplete.value = true;
},
});
}
}, props.options);
if (root.value) {
observer.observe(root.value);
}
});
onUnmounted(() => {
observer?.disconnect();
});
</script>
<template>
<div ref="root">
<slot v-if="isIntersecting" name="placeholder">
<div>Loading...</div>
</slot>
<slot v-if="isComplete" name="no-more">
<div>No more data.</div>
</slot>
</div>
</template>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment