Last active
November 23, 2022 17:13
-
-
Save wobsoriano/41a01290a9967c7f39077fe5c4a70436 to your computer and use it in GitHub Desktop.
Vue 3 Infinite Scroll Component
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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