How to setup infinite scroll (v2) #2049
Unanswered
AlexanderFalkenberg
asked this question in
Help (React)
Replies: 1 comment
-
For those wondering how to implement an infinite feed using the intersection observer and deferred loading (v2) features, this is the approach I went with when playing around with it over the last couple of days. You could certainly simplify it further, the was just the cleanest approach I could come up with. Personally I prefer not to merge the props on the server and handle it in the client like this. <script setup>
import NavLayout from '../../Layouts/Navbar/NavbarLayout.vue';
import DashboardLayout from '../../Layouts/DashboardLayout.vue';
import { reactive, ref, watch } from 'vue';
import FeedCard from './FeedCard.vue';
import { Deferred, usePage, WhenVisible } from '@inertiajs/vue3';
import PostSkeleton from '@/Pages/Feed/PostSkeleton.vue';
defineOptions({
layout: [NavLayout, DashboardLayout],
});
let props = defineProps({
feed: Object, // deferred prop
});
const initialUrl = usePage().url;
const isFetching = ref(true);
const items = reactive({ initial: [], subsequent: [] });
const hasMorePages = ref(true);
const nextPage = ref(2);
const perPage = ref(10);
watch(
() => props.feed,
(feed) => updateFeed(feed),
);
const updateFeed = (feed) => {
const perPage = feed.meta.per_page;
if (feed.meta.current_page === 1 && items.initial.length !== perPage) {
items.initial = feed.data;
} else {
items.subsequent = items.subsequent.concat(feed.data);
}
hasMorePages.value = feed.meta.current_page < feed.meta.last_page;
nextPage.value = feed.meta.current_page + 1;
resetFetchState();
};
const resetFetchState = () => {
if (typeof window !== 'undefined') {
window.history.replaceState({}, '', initialUrl);
}
setIsFetching(false);
};
const setIsFetching = (value) => {
isFetching.value = value;
};
</script>
<template>
<div class="h-fit">
<Deferred data="feed">
<template #fallback>
<PostSkeleton v-for="n in perPage" />
</template>
<FeedCard v-for="item in items.initial" :key="item.uuid" :item="item" />
</Deferred>
<FeedCard v-for="item in items.subsequent" :key="item.uuid" :item="item" />
<WhenVisible
:always="hasMorePages"
:params="{
data: { page: nextPage },
only: ['feed'],
onBefore: () => setIsFetching(true),
onFinish: () => setIsFetching(false),
}"
>
<template v-show="isFetching" #fallback>
<div class="load-card">
<span>Loaded first time visible...</span>
<span class="loader-spinner"></span>
</div>
</template>
<div class="load-card" v-if="isFetching">
<span>Loaded subsequent times visible...</span>
<span class="loader-spinner"></span>
</div>
</WhenVisible>
</div>
</template>
<style scoped>
.loader-spinner {
@apply ml-4 h-10 w-10 animate-spin rounded-full border-4 border-gray-900 border-t-sky-400 border-t-[4px];
}
.load-card {
@apply mt-4 flex items-center justify-center rounded-lg border-2 border-sky-400 bg-sky-100 p-8 dark:bg-gray-800;
}
</style> And my controller: final class FeedController extends Controller
{
public function __invoke(Request $request, ?string $type = 'insight'): Response
{
Gate::allowIf($request->user()?->can('viewAny', Feed::class));
Validator::make(
$request->route()->parameters(),
['type' => ['required', Rule::enum(FeedType::class)]]
)->validate();
flash('success', 'Feed loaded!');
return Inertia::render('Feed/Index', [
'feed' => Inertia::defer(
static fn () => GetFeed::fromRequest($request)
->ofType($type)
->before(
session()?->remember('feed.fetched_at', fn () => now())
)
->handle()
),
])
->with('crumbs', ['Community', 'Feed', 'Index']);
}
} |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
With 2.0 it should be easier to implement infinite scroll using WhenVisible and Props Merging. I tried several approached, but it's not working. It seems the syntax is still changing.
Could someone point me in the right direction?
Beta Was this translation helpful? Give feedback.
All reactions