Infinite Scroll

Project
WireUse
Updated
Last updated 12 hours ago

Introduction

Infinite scrolling is an approach which loads content continuously as the user scrolls down or on button click.

While many tutorials increase the query limit (->take()), this trait focuses on fetching as paginate.

This should decrease database load, and make fetching while merging items a lot more efficient.

Usage

Tip: Optionally you may want to include Livewire\WithoutUrlPagination to hide ?page= args.

To use infinite scrolling, implement the WithPaginateScroll trait on a Livewire component:

use Domain\Tags\Models\Tag;
use Foxws\WireUse\Models\Concerns\WithPaginateScroll;
use Illuminate\Pagination\Paginator;
use Livewire\Component;

class Section extends Component
{
    use WithPaginateScroll;

    /**
     * This builds upon the WithQueryBuilder trait.
     */
    protected function getBuilder(): Paginator
    {
        return $this->getQuery()
            ->where('state', 'published')
            ->simplePaginate(
                perPage: $this->getCandidatesLimit(),
                page: $this->getPage(),
            );
    }

    /**
     * Defines the model class used to call the query builder.
     */
    protected function getModelClass(): ?string
    {
        return Tag::class;
    }

    /**
     * This a limit that will used when fetching and merging items.
     */
    protected function getCandidatesLimit(): int
    {
        return 12;
    }

    /**
     * How many times it is possible to call fetch.
     */
    protected function getFetchLimits(): ?int
    {
        return 3; // total items 12 * 3 = 36
    }
}

Create a Blade view (e.g. tags.blade.php):

<div>
    <div
        wire:scroll
        wire:poll.900s
        class="grid grid-cols-2 overflow-y-scroll"
    >
        @foreach ($this->items as $tag)
            <div wire:key="{{ $tag->getRouteKey() }}">
                <livewire:tags.item :$tag :key="$tag->getRouteKey()" />
            </div>
        @endforeach
    </div>

    <nav class="flex items-center w-full">
        @if ($this->isFetchable())
            <button
                wire:loading.attr="disabled"
                wire:click="nextPage"
            >
                Load more
            </button>
        @endif
    </nav>
</div>

It is also possible to use an 'on-scroll' approach using an Alpine intersect:

<div x-data>
    <div
        wire:poll.900s
        class="grid grid-cols-2 overflow-y-scroll"
    >
        @foreach ($this->items as $tag)
            <div wire:key="{{ $tag->getRouteKey() }}">
                <livewire:tags.item :$tag :key="$tag->getRouteKey()" />
            </div>
        @endforeach
    </div>

    <div
        class="w-full h-0 min-h-0"
        x-intersect.full="$wire.nextPage()"
    />
</div>